Merge tag 'staging-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Nov 2013 06:07:58 +0000 (15:07 +0900)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Nov 2013 06:07:58 +0000 (15:07 +0900)
Pull staging driver update from Greg KH:
 "Here's the big drivers/staging/ update for 3.13-rc1.

  Nothing major here, just a _ton_ of fixes and cleanups, mostly driven
  by the new round of OPW applicants, but also there are lots of other
  people doing staging tree cleanups these days in order to help get the
  drivers into mergable shape.

  We also merge, and then revert, the ktap code, as Ingo and the other
  perf/ftrace developers feel it should go into the "real" part of the
  kernel with only a bit more work, so no need to put it in staging for
  now.

  All of this has been in linux-next for a while with no reported
  issues"

* tag 'staging-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1045 commits)
  staging: drm/imx: fix return value check in ipu_add_subdevice_pdata()
  Staging: zram: Fix access of NULL pointer
  Staging: zram: Fix variable dereferenced before check
  Staging: rtl8187se: space prohibited before semicolon in r8185b_init.c
  Staging: rtl8187se: fix space prohibited after that open parenthesis '(' in r8185b_init.c
  Staging: rtl8187se: fix braces {} are not necessary for single statement blocks in r8185b_init.c
  Staging: rtl8187se: fix trailing whitespace in r8185b_init.c
  Staging: rtl8187se: fix please, no space before tabs in r8185b_init.c
  drivers/staging/nvec/Kconfig: remove trailing whitespace
  Staging: dwc2: Fix variable dereferenced before check
  Staging: xgifb: fix braces {} are not necessary for any arm of this statement
  staging: rtl8192e: remove unneeded semicolons
  staging: rtl8192e: use true and false for bool variables
  staging: ft1000: return values corrected in scram_start_dwnld
  staging: ft1000: change values of status return variable in write_dpram32_and_check
  staging: bcm: Remove unnecessary pointer casting
  imx-drm: ipuv3-crtc: Invert IPU DI0 clock polarity
  staging: r8188eu: Fix sparse warnings in rtl_p2p.c
  staging: r8188eu: Fix sparse warnings in rtw_mlme_ext.c
  staging: r8188eu: Fix sparse warnings in rtl8188e.cmd.c
  ...

862 files changed:
Documentation/ABI/testing/configfs-usb-gadget-mass-storage [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-mic.txt [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-sunxi-sid [new file with mode: 0644]
Documentation/DocBook/filesystems.tmpl
Documentation/connector/ucon.c
Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt [new file with mode: 0644]
Documentation/devicetree/bindings/misc/ti,dac7512.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-bindings.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/samsung-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/msm-hsusb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/omap-usb.txt
Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt
Documentation/devicetree/bindings/usb/usb-phy.txt
Documentation/devicetree/bindings/video/exynos_dp.txt
Documentation/extcon/porting-android-switch-class
Documentation/mic/mic_overview.txt [new file with mode: 0644]
Documentation/mic/mpssd/.gitignore [new file with mode: 0644]
Documentation/mic/mpssd/Makefile [new file with mode: 0644]
Documentation/mic/mpssd/micctrl [new file with mode: 0755]
Documentation/mic/mpssd/mpss [new file with mode: 0755]
Documentation/mic/mpssd/mpssd.c [new file with mode: 0644]
Documentation/mic/mpssd/mpssd.h [new file with mode: 0644]
Documentation/mic/mpssd/sysfs.c [new file with mode: 0644]
Documentation/networking/dccp.txt
Documentation/networking/e100.txt
Documentation/networking/ieee802154.txt
Documentation/networking/l2tp.txt
Documentation/networking/netdev-FAQ.txt
Documentation/networking/netlink_mmap.txt
Documentation/networking/operstates.txt
Documentation/networking/rxrpc.txt
Documentation/networking/stmmac.txt
Documentation/networking/vortex.txt
Documentation/networking/x25-iface.txt
Documentation/phy.txt [new file with mode: 0644]
Documentation/pps/pps.txt
Documentation/serial/driver
Documentation/sysrq.txt
MAINTAINERS
Makefile
arch/arc/mm/fault.c
arch/arm/boot/dts/integratorcp.dts
arch/arm/boot/dts/omap3-beagle-xm.dts
arch/arm/boot/dts/omap3-evm.dts
arch/arm/boot/dts/omap3-overo.dtsi
arch/arm/boot/dts/omap4.dtsi
arch/arm/boot/dts/omap5.dtsi
arch/arm/boot/dts/twl4030.dtsi
arch/arm/configs/ep93xx_defconfig
arch/arm/mach-ep93xx/clock.c
arch/arm/mach-ep93xx/core.c
arch/arm/mach-exynos/include/mach/regs-pmu.h
arch/arm/mach-omap2/board-omap3beagle.c
arch/arm/mach-omap2/twl-common.c
arch/arm/mach-omap2/usb-host.c
arch/arm/mach-omap2/usb.h
arch/arm/mach-s5pv210/include/mach/regs-clock.h
arch/arm/net/bpf_jit_32.c
arch/arm/plat-samsung/Kconfig
arch/arm/plat-samsung/Makefile
arch/arm/plat-samsung/devs.c
arch/arm/plat-samsung/include/plat/devs.h
arch/arm/plat-samsung/setup-mipiphy.c [deleted file]
arch/mips/kernel/perf_event_mipsxx.c
arch/mips/mti-malta/malta-int.c
arch/mips/ralink/timer.c
arch/parisc/configs/712_defconfig
arch/parisc/configs/a500_defconfig
arch/parisc/configs/b180_defconfig
arch/parisc/configs/c3000_defconfig
arch/parisc/configs/c8000_defconfig
arch/parisc/configs/default_defconfig
arch/parisc/kernel/cache.c
arch/parisc/kernel/head.S
arch/powerpc/kernel/ibmebus.c
arch/powerpc/kernel/vio.c
arch/powerpc/net/bpf_jit_comp.c
arch/s390/include/asm/pgtable.h
arch/s390/include/asm/timex.h
arch/s390/kernel/compat_signal.c
arch/s390/kernel/debug.c
arch/s390/kvm/interrupt.c
arch/s390/lib/delay.c
arch/s390/net/bpf_jit_comp.c
arch/sparc/net/bpf_jit_comp.c
arch/um/kernel/exitcode.c
arch/x86/include/asm/percpu.h
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/kvm.c
arch/x86/kernel/nmi.c
arch/x86/net/bpf_jit_comp.c
arch/xtensa/kernel/entry.S
arch/xtensa/kernel/signal.c
arch/xtensa/platforms/iss/network.c
drivers/Kconfig
drivers/Makefile
drivers/ata/ahci.c
drivers/ata/ahci_platform.c
drivers/ata/libahci.c
drivers/ata/libata-eh.c
drivers/ata/pata_isapnp.c
drivers/base/bus.c
drivers/base/class.c
drivers/base/core.c
drivers/base/devres.c
drivers/base/firmware_class.c
drivers/base/platform.c
drivers/bcma/main.c
drivers/char/hpet.c
drivers/char/misc.c
drivers/char/nwbutton.c
drivers/char/rtc.c
drivers/char/snsc.c
drivers/char/snsc_event.c
drivers/char/tlclk.c
drivers/char/xilinx_hwicap/xilinx_hwicap.c
drivers/clk/clk-nomadik.c
drivers/clk/mvebu/armada-370.c
drivers/clk/socfpga/clk.c
drivers/clk/versatile/clk-icst.c
drivers/connector/cn_proc.c
drivers/connector/connector.c
drivers/cpufreq/acpi-cpufreq.c
drivers/cpufreq/intel_pstate.c
drivers/dma/edma.c
drivers/extcon/extcon-adc-jack.c
drivers/extcon/extcon-arizona.c
drivers/extcon/extcon-class.c
drivers/extcon/extcon-gpio.c
drivers/extcon/extcon-max77693.c
drivers/extcon/extcon-max8997.c
drivers/extcon/extcon-palmas.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/dce6_afmt.c
drivers/gpu/drm/radeon/evergreen_hdmi.c
drivers/gpu/drm/radeon/kv_dpm.c
drivers/gpu/drm/radeon/ni.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_hdmi.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_uvd.c
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/uvd_v1_0.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-wiimote-core.c
drivers/hid/usbhid/hid-quirks.c
drivers/hsi/hsi.c
drivers/hv/channel.c
drivers/hv/channel_mgmt.c
drivers/hv/connection.c
drivers/hv/hv.c
drivers/hv/hv_util.c
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c
drivers/ide/ide-sysfs.c
drivers/ide/ide.c
drivers/infiniband/Kconfig
drivers/infiniband/core/uverbs.h
drivers/infiniband/core/uverbs_cmd.c
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/hw/mlx4/main.c
drivers/infiniband/ulp/isert/ib_isert.c
drivers/input/gameport/gameport.c
drivers/input/input.c
drivers/input/keyboard/pxa27x_keypad.c
drivers/input/misc/cm109.c
drivers/input/mouse/alps.c
drivers/input/serio/i8042.c
drivers/input/serio/serio.c
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/ipack/ipack.c
drivers/md/bcache/request.c
drivers/md/bitmap.c
drivers/md/md.c
drivers/md/md.h
drivers/md/raid1.c
drivers/md/raid10.c
drivers/md/raid5.c
drivers/media/dvb-frontends/tda10071.c
drivers/media/i2c/ad9389b.c
drivers/media/i2c/adv7511.c
drivers/media/i2c/adv7842.c
drivers/media/i2c/ths8200.c
drivers/media/pci/saa7134/saa7134-video.c
drivers/media/platform/exynos4-is/Kconfig
drivers/media/platform/exynos4-is/mipi-csis.c
drivers/media/platform/s5p-jpeg/jpeg-core.c
drivers/media/platform/sh_vou.c
drivers/media/platform/soc_camera/mx3_camera.c
drivers/media/tuners/e4000.c
drivers/media/usb/stkwebcam/stk-webcam.c
drivers/media/usb/uvc/uvc_driver.c
drivers/media/v4l2-core/videobuf2-core.c
drivers/media/v4l2-core/videobuf2-dma-contig.c
drivers/memstick/core/memstick.c
drivers/message/i2o/core.h
drivers/message/i2o/device.c
drivers/message/i2o/driver.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/arm-charlcd.c
drivers/misc/atmel_pwm.c
drivers/misc/bh1780gli.c
drivers/misc/bmp085.c
drivers/misc/cb710/core.c
drivers/misc/eeprom/Kconfig
drivers/misc/eeprom/Makefile
drivers/misc/eeprom/at24.c
drivers/misc/eeprom/at25.c
drivers/misc/eeprom/eeprom_93xx46.c
drivers/misc/eeprom/sunxi_sid.c [new file with mode: 0644]
drivers/misc/ibmasm/module.c
drivers/misc/lkdtm.c
drivers/misc/mei/amthif.c
drivers/misc/mei/bus.c
drivers/misc/mei/client.c
drivers/misc/mei/client.h
drivers/misc/mei/hbm.c
drivers/misc/mei/hw-me-regs.h
drivers/misc/mei/init.c
drivers/misc/mei/interrupt.c
drivers/misc/mei/main.c
drivers/misc/mei/mei_dev.h
drivers/misc/mei/nfc.c
drivers/misc/mei/pci-me.c
drivers/misc/mei/wd.c
drivers/misc/mic/Kconfig [new file with mode: 0644]
drivers/misc/mic/Makefile [new file with mode: 0644]
drivers/misc/mic/card/Makefile [new file with mode: 0644]
drivers/misc/mic/card/mic_debugfs.c [new file with mode: 0644]
drivers/misc/mic/card/mic_device.c [new file with mode: 0644]
drivers/misc/mic/card/mic_device.h [new file with mode: 0644]
drivers/misc/mic/card/mic_virtio.c [new file with mode: 0644]
drivers/misc/mic/card/mic_virtio.h [new file with mode: 0644]
drivers/misc/mic/card/mic_x100.c [new file with mode: 0644]
drivers/misc/mic/card/mic_x100.h [new file with mode: 0644]
drivers/misc/mic/common/mic_dev.h [new file with mode: 0644]
drivers/misc/mic/host/Makefile [new file with mode: 0644]
drivers/misc/mic/host/mic_boot.c [new file with mode: 0644]
drivers/misc/mic/host/mic_debugfs.c [new file with mode: 0644]
drivers/misc/mic/host/mic_device.h [new file with mode: 0644]
drivers/misc/mic/host/mic_fops.c [new file with mode: 0644]
drivers/misc/mic/host/mic_fops.h [new file with mode: 0644]
drivers/misc/mic/host/mic_intr.c [new file with mode: 0644]
drivers/misc/mic/host/mic_intr.h [new file with mode: 0644]
drivers/misc/mic/host/mic_main.c [new file with mode: 0644]
drivers/misc/mic/host/mic_smpt.c [new file with mode: 0644]
drivers/misc/mic/host/mic_smpt.h [new file with mode: 0644]
drivers/misc/mic/host/mic_sysfs.c [new file with mode: 0644]
drivers/misc/mic/host/mic_virtio.c [new file with mode: 0644]
drivers/misc/mic/host/mic_virtio.h [new file with mode: 0644]
drivers/misc/mic/host/mic_x100.c [new file with mode: 0644]
drivers/misc/mic/host/mic_x100.h [new file with mode: 0644]
drivers/misc/phantom.c
drivers/misc/pti.c
drivers/misc/ti_dac7512.c
drivers/misc/tifm_7xx1.c
drivers/misc/tifm_core.c
drivers/misc/vmw_vmci/vmci_guest.c
drivers/misc/vmw_vmci/vmci_host.c
drivers/misc/vmw_vmci/vmci_queue_pair.c
drivers/mmc/core/bus.c
drivers/mmc/core/sdio_bus.c
drivers/mmc/host/mvsdio.c
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/net/bonding/bond_sysfs.c
drivers/net/can/at91_can.c
drivers/net/can/c_can/c_can.c
drivers/net/can/dev.c
drivers/net/can/flexcan.c
drivers/net/can/usb/kvaser_usb.c
drivers/net/ethernet/broadcom/bgmac.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
drivers/net/ethernet/calxeda/xgmac.c
drivers/net/ethernet/chelsio/cxgb3/sge.c
drivers/net/ethernet/davicom/dm9000.c
drivers/net/ethernet/emulex/benet/be.h
drivers/net/ethernet/emulex/benet/be_cmds.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/ibm/emac/mal.c
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/mellanox/mlx4/cmd.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/moxa/moxart_ether.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/sfc/ef10.c
drivers/net/ethernet/sfc/mcdi.c
drivers/net/ethernet/sfc/mcdi_pcol.h
drivers/net/ethernet/sfc/nic.c
drivers/net/ethernet/sfc/nic.h
drivers/net/ethernet/smsc/smc91x.h
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/hamradio/yam.c
drivers/net/ieee802154/mrf24j40.c
drivers/net/netconsole.c
drivers/net/phy/mdio_bus.c
drivers/net/tun.c
drivers/net/usb/ax88179_178a.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/usbnet.c
drivers/net/virtio_net.c
drivers/net/wan/farsync.c
drivers/net/wan/sbni.c
drivers/net/wan/wanxl.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/cw1200/cw1200_spi.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/rt2x00/rt2x00pci.c
drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
drivers/net/xen-netback/common.h
drivers/net/xen-netback/interface.c
drivers/net/xen-netback/netback.c
drivers/net/xen-netback/xenbus.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/pci-driver.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.h
drivers/pcmcia/at91_cf.c
drivers/pcmcia/ds.c
drivers/pcmcia/pd6729.c
drivers/pcmcia/yenta_socket.c
drivers/phy/Kconfig [new file with mode: 0644]
drivers/phy/Makefile [new file with mode: 0644]
drivers/phy/phy-core.c [new file with mode: 0644]
drivers/phy/phy-exynos-dp-video.c [new file with mode: 0644]
drivers/phy/phy-exynos-mipi-video.c [new file with mode: 0644]
drivers/phy/phy-omap-usb2.c [new file with mode: 0644]
drivers/phy/phy-twl4030-usb.c [new file with mode: 0644]
drivers/platform/x86/Kconfig
drivers/platform/x86/sony-laptop.c
drivers/pnp/base.h
drivers/pnp/driver.c
drivers/pnp/interface.c
drivers/rapidio/rio-driver.c
drivers/rapidio/rio-sysfs.c
drivers/rapidio/rio.h
drivers/s390/block/dasd_eckd.c
drivers/s390/char/sclp.c
drivers/s390/char/vmlogrdr.c
drivers/s390/cio/cio.c
drivers/s390/cio/qdio_main.c
drivers/scsi/BusLogic.c
drivers/scsi/aacraid/linit.c
drivers/scsi/fcoe/fcoe_sysfs.c
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/sd.c
drivers/scsi/sg.c
drivers/ssb/main.c
drivers/staging/bcm/Bcmchar.c
drivers/staging/media/msi3101/Kconfig
drivers/staging/media/msi3101/sdr-msi3101.c
drivers/staging/ozwpan/ozcdev.c
drivers/staging/sb105x/sb_pci_mp.c
drivers/staging/wlags49_h2/wl_priv.c
drivers/target/target_core_pscsi.c
drivers/target/target_core_sbc.c
drivers/target/target_core_xcopy.c
drivers/thermal/samsung/exynos_thermal_common.c
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/samsung/exynos_tmu.h
drivers/thermal/samsung/exynos_tmu_data.c
drivers/thermal/samsung/exynos_tmu_data.h
drivers/thermal/thermal_hwmon.c
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
drivers/thermal/x86_pkg_temp_thermal.c
drivers/tty/bfin_jtag_comm.c
drivers/tty/hvc/hvc_dcc.c
drivers/tty/hvc/hvc_vio.c
drivers/tty/n_tty.c
drivers/tty/nozomi.c
drivers/tty/serial/8250/8250_core.c
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_em.c
drivers/tty/serial/8250/8250_pci.c
drivers/tty/serial/Kconfig
drivers/tty/serial/amba-pl010.c
drivers/tty/serial/amba-pl011.c
drivers/tty/serial/arc_uart.c
drivers/tty/serial/atmel_serial.c
drivers/tty/serial/bfin_sport_uart.c
drivers/tty/serial/bfin_uart.c
drivers/tty/serial/clps711x.c
drivers/tty/serial/ifx6x60.c
drivers/tty/serial/imx.c
drivers/tty/serial/ip22zilog.c
drivers/tty/serial/max310x.c
drivers/tty/serial/mfd.c
drivers/tty/serial/mpc52xx_uart.c
drivers/tty/serial/mpsc.c
drivers/tty/serial/mrst_max3110.c
drivers/tty/serial/mxs-auart.c
drivers/tty/serial/omap-serial.c
drivers/tty/serial/pch_uart.c
drivers/tty/serial/pmac_zilog.c
drivers/tty/serial/sa1100.c
drivers/tty/serial/samsung.c
drivers/tty/serial/samsung.h
drivers/tty/serial/sccnxp.c
drivers/tty/serial/serial-tegra.c
drivers/tty/serial/serial_txx9.c
drivers/tty/serial/sirfsoc_uart.c
drivers/tty/serial/sirfsoc_uart.h
drivers/tty/serial/sunsab.c
drivers/tty/serial/sunsu.c
drivers/tty/serial/sunzilog.c
drivers/tty/serial/ucc_uart.c
drivers/tty/serial/xilinx_uartps.c
drivers/tty/sysrq.c
drivers/tty/tty_port.c
drivers/tty/vt/vt.c
drivers/uio/uio.c
drivers/uio/uio_aec.c
drivers/uio/uio_cif.c
drivers/uio/uio_mf624.c
drivers/uio/uio_netx.c
drivers/uio/uio_pdrv_genirq.c
drivers/uio/uio_sercos3.c
drivers/usb/atm/usbatm.h
drivers/usb/chipidea/bits.h
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/core.c
drivers/usb/chipidea/host.c
drivers/usb/chipidea/udc.c
drivers/usb/class/cdc-wdm.c
drivers/usb/core/devio.c
drivers/usb/core/driver.c
drivers/usb/core/file.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/quirks.c
drivers/usb/core/sysfs.c
drivers/usb/core/urb.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/dwc3/core.c
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/ep0.c
drivers/usb/early/ehci-dbgp.c
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/acm_ms.c
drivers/usb/gadget/amd5536udc.c
drivers/usb/gadget/composite.c
drivers/usb/gadget/configfs.c
drivers/usb/gadget/configfs.h [new file with mode: 0644]
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/f_mass_storage.c
drivers/usb/gadget/f_mass_storage.h [new file with mode: 0644]
drivers/usb/gadget/g_ffs.c
drivers/usb/gadget/goku_udc.c
drivers/usb/gadget/mass_storage.c
drivers/usb/gadget/multi.c
drivers/usb/gadget/mv_u3d_core.c
drivers/usb/gadget/net2280.c
drivers/usb/gadget/pch_udc.c
drivers/usb/gadget/rndis.c
drivers/usb/gadget/s3c-hsotg.c
drivers/usb/gadget/storage_common.c
drivers/usb/gadget/storage_common.h [new file with mode: 0644]
drivers/usb/gadget/tcm_usb_gadget.c
drivers/usb/gadget/udc-core.c
drivers/usb/gadget/zero.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-atmel.c
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-exynos.c [new file with mode: 0644]
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-grlib.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-mem.c
drivers/usb/host/ehci-msm.c
drivers/usb/host/ehci-mv.c
drivers/usb/host/ehci-octeon.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-pmcmsp.c
drivers/usb/host/ehci-ppc-of.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-s5p.c [deleted file]
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci-sead3.c
drivers/usb/host/ehci-sh.c
drivers/usb/host/ehci-sysfs.c
drivers/usb/host/ehci-tegra.c
drivers/usb/host/ehci-tilegx.c
drivers/usb/host/ehci-w90x900.c
drivers/usb/host/ehci-xilinx-of.c
drivers/usb/host/ehci.h
drivers/usb/host/fotg210-hcd.c
drivers/usb/host/fusbh200-hcd.c
drivers/usb/host/hwa-hc.c
drivers/usb/host/isp1362-hcd.c
drivers/usb/host/ohci-at91.c
drivers/usb/host/ohci-dbg.c
drivers/usb/host/ohci-ep93xx.c [deleted file]
drivers/usb/host/ohci-exynos.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-hub.c
drivers/usb/host/ohci-nxp.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-omap3.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci-platform.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/ohci-s3c2410.c
drivers/usb/host/ohci-sm501.c
drivers/usb/host/ohci-spear.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/pci-quirks.h
drivers/usb/host/sl811-hcd.c
drivers/usb/host/uhci-debug.c
drivers/usb/host/uhci-hub.c
drivers/usb/host/uhci-pci.c
drivers/usb/host/uhci-platform.c
drivers/usb/host/whci/hcd.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/misc/usbtest.c
drivers/usb/musb/Kconfig
drivers/usb/musb/am35x.c
drivers/usb/musb/blackfin.c
drivers/usb/musb/da8xx.c
drivers/usb/musb/davinci.c
drivers/usb/musb/musb_am335x.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h
drivers/usb/musb/musb_cppi41.c
drivers/usb/musb/musb_dsps.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/musb_virthub.c
drivers/usb/musb/omap2430.c
drivers/usb/musb/tusb6010.c
drivers/usb/musb/ux500.c
drivers/usb/phy/Kconfig
drivers/usb/phy/Makefile
drivers/usb/phy/phy-am335x-control.c
drivers/usb/phy/phy-am335x.c
drivers/usb/phy/phy-fsl-usb.c
drivers/usb/phy/phy-fsl-usb.h
drivers/usb/phy/phy-fsm-usb.c
drivers/usb/phy/phy-fsm-usb.h
drivers/usb/phy/phy-generic.c
drivers/usb/phy/phy-generic.h
drivers/usb/phy/phy-omap-control.c
drivers/usb/phy/phy-omap-usb2.c [deleted file]
drivers/usb/phy/phy-omap-usb3.c
drivers/usb/phy/phy-rcar-gen2-usb.c [new file with mode: 0644]
drivers/usb/phy/phy-samsung-usb2.c
drivers/usb/phy/phy-samsung-usb3.c
drivers/usb/phy/phy-tegra-usb.c
drivers/usb/phy/phy-twl4030-usb.c [deleted file]
drivers/usb/phy/phy-twl6030-usb.c
drivers/usb/phy/phy-ulpi-viewport.c
drivers/usb/phy/phy.c
drivers/usb/serial/cyberjack.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio_ids.h
drivers/usb/serial/generic.c
drivers/usb/serial/mos7840.c
drivers/usb/serial/option.c
drivers/usb/serial/pl2303.c
drivers/usb/wusbcore/cbaf.c
drivers/usb/wusbcore/devconnect.c
drivers/usb/wusbcore/wa-hc.c
drivers/usb/wusbcore/wa-hc.h
drivers/usb/wusbcore/wa-rpipe.c
drivers/usb/wusbcore/wa-xfer.c
drivers/uwb/lc-dev.c
drivers/uwb/umc-bus.c
drivers/vhost/scsi.c
drivers/video/au1100fb.c
drivers/video/au1200fb.c
drivers/video/backlight/atmel-pwm-bl.c
drivers/video/exynos/Kconfig
drivers/video/exynos/exynos_dp_core.c
drivers/video/exynos/exynos_dp_core.h
drivers/video/exynos/exynos_dp_reg.c
drivers/video/exynos/exynos_mipi_dsi.c
drivers/virtio/virtio.c
drivers/w1/masters/ds1wm.c
drivers/w1/masters/omap_hdq.c
drivers/w1/masters/w1-gpio.c
drivers/xen/xenbus/xenbus_probe.c
drivers/xen/xenbus/xenbus_probe.h
drivers/xen/xenbus/xenbus_probe_backend.c
drivers/xen/xenbus/xenbus_probe_frontend.c
fs/dcache.c
fs/ecryptfs/crypto.c
fs/ecryptfs/keystore.c
fs/eventpoll.c
fs/file_table.c
fs/jfs/jfs_inode.c
fs/namei.c
fs/select.c
fs/seq_file.c
fs/sysfs/Makefile
fs/sysfs/bin.c [deleted file]
fs/sysfs/dir.c
fs/sysfs/file.c
fs/sysfs/group.c
fs/sysfs/inode.c
fs/sysfs/mount.c
fs/sysfs/symlink.c
fs/sysfs/sysfs.h
include/linux/atmel_serial.h
include/linux/debugfs.h
include/linux/device.h
include/linux/extcon.h
include/linux/extcon/extcon-adc-jack.h
include/linux/extcon/extcon-gpio.h
include/linux/filter.h
include/linux/hyperv.h
include/linux/i2c/twl.h
include/linux/ide.h
include/linux/ipc_namespace.h
include/linux/kobj_completion.h [new file with mode: 0644]
include/linux/kobject.h
include/linux/netdevice.h
include/linux/netpoll.h
include/linux/percpu.h
include/linux/phy/phy.h [new file with mode: 0644]
include/linux/platform_data/mipi-csis.h
include/linux/platform_data/usb-ehci-s5p.h [deleted file]
include/linux/platform_data/usb-ohci-exynos.h [deleted file]
include/linux/platform_data/usb-rcar-gen2-phy.h [new file with mode: 0644]
include/linux/platform_device.h
include/linux/printk.h
include/linux/serial_core.h
include/linux/sysfs.h
include/linux/sysrq.h
include/linux/tc_act/tc_defact.h [deleted file]
include/linux/tty.h
include/linux/usb.h
include/linux/usb/hcd.h
include/linux/usb/intel_mid_otg.h [deleted file]
include/linux/usb/musb.h
include/linux/usb/omap_control_usb.h
include/linux/usb/serial.h
include/linux/usb/usb_phy_gen_xceiv.h
include/linux/usb/wusb-wa.h
include/linux/yam.h
include/net/cipso_ipv4.h
include/net/dst.h
include/net/ip6_fib.h
include/net/ip6_route.h
include/net/mac802154.h
include/net/sock.h
include/sound/rcar_snd.h
include/trace/events/target.h
include/uapi/drm/drm_mode.h
include/uapi/linux/Kbuild
include/uapi/linux/mic_common.h [new file with mode: 0644]
include/uapi/linux/mic_ioctl.h [new file with mode: 0644]
include/uapi/linux/perf_event.h
include/uapi/linux/tc_act/Kbuild
include/uapi/linux/tc_act/tc_defact.h [new file with mode: 0644]
include/uapi/rdma/ib_user_verbs.h
include/video/exynos_dp.h [deleted file]
include/video/exynos_mipi_dsim.h
ipc/ipc_sysctl.c
kernel/cgroup.c
kernel/events/core.c
kernel/events/ring_buffer.c
kernel/mutex.c
kernel/power/hibernate.c
kernel/sysctl.c
kernel/time/clockevents.c
lib/Kconfig.debug
lib/kobject.c
lib/scatterlist.c
mm/huge_memory.c
mm/list_lru.c
mm/memcontrol.c
mm/memory.c
mm/migrate.c
mm/mprotect.c
mm/pagewalk.c
net/8021q/vlan_netlink.c
net/batman-adv/main.c
net/batman-adv/network-coding.c
net/batman-adv/network-coding.h
net/bridge/br_device.c
net/bridge/br_fdb.c
net/bridge/br_input.c
net/bridge/br_mdb.c
net/bridge/br_multicast.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_stp_if.c
net/bridge/br_vlan.c
net/bridge/netfilter/ebt_ulog.c
net/compat.c
net/core/dev.c
net/core/filter.c
net/core/flow_dissector.c
net/core/net-sysfs.c
net/core/netpoll.c
net/core/secure_seq.c
net/core/sock.c
net/ieee802154/6lowpan.c
net/ipv4/inet_hashtables.c
net/ipv4/ip_output.c
net/ipv4/ip_vti.c
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_ULOG.c
net/ipv4/route.c
net/ipv4/tcp_input.c
net/ipv4/tcp_offload.c
net/ipv4/tcp_output.c
net/ipv4/xfrm4_policy.c
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/inet6_hashtables.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_output.c
net/ipv6/ip6_tunnel.c
net/ipv6/ipcomp6.c
net/ipv6/netfilter/ip6_tables.c
net/ipv6/route.c
net/ipv6/udp.c
net/ipv6/xfrm6_policy.c
net/key/af_key.c
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_ppp.c
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/offchannel.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/netfilter/nf_conntrack_h323_main.c
net/netfilter/x_tables.c
net/netfilter/xt_NFQUEUE.c
net/openvswitch/dp_notify.c
net/openvswitch/vport-netdev.c
net/openvswitch/vport-netdev.h
net/sched/sch_fq.c
net/sched/sch_netem.c
net/sctp/ipv6.c
net/sctp/output.c
net/sctp/sm_sideeffect.c
net/socket.c
net/unix/af_unix.c
net/unix/diag.c
net/wireless/core.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/nl80211.c
net/wireless/radiotap.c
net/x25/Kconfig
net/xfrm/xfrm_ipcomp.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_replay.c
net/xfrm/xfrm_user.c
scripts/kallsyms.c
scripts/link-vmlinux.sh
sound/core/pcm.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_generic.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_realtek.c
sound/soc/codecs/pcm1681.c
sound/soc/codecs/pcm1792a.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/wm_hubs.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-mc13783.c
sound/soc/fsl/imx-ssi.c
sound/soc/fsl/imx-ssi.h
sound/soc/omap/Kconfig
sound/soc/sh/rcar/rsnd.h
sound/soc/soc-dapm.c
tools/hv/hv_kvp_daemon.c
tools/hv/hv_vss_daemon.c
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-top.txt
tools/perf/builtin-kvm.c
tools/perf/builtin-record.c
tools/perf/builtin-top.c
tools/perf/builtin-trace.c
tools/perf/tests/code-reading.c
tools/perf/tests/keep-tracking.c
tools/perf/tests/mmap-basic.c
tools/perf/tests/open-syscall-tp-fields.c
tools/perf/tests/perf-record.c
tools/perf/tests/perf-time-to-tsc.c
tools/perf/tests/sw-clock.c
tools/perf/tests/task-exit.c
tools/perf/ui/stdio/hist.c
tools/perf/util/callchain.h
tools/perf/util/event.c
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/hist.h
tools/perf/util/probe-finder.c
tools/perf/util/python.c
tools/perf/util/scripting-engines/trace-event-perl.c
tools/perf/util/scripting-engines/trace-event-python.c
virt/kvm/kvm_main.c

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-mass-storage b/Documentation/ABI/testing/configfs-usb-gadget-mass-storage
new file mode 100644 (file)
index 0000000..ad72a37
--- /dev/null
@@ -0,0 +1,31 @@
+What:          /config/usb-gadget/gadget/functions/mass_storage.name
+Date:          Oct 2013
+KenelVersion:  3.13
+Description:
+               The attributes:
+
+               stall           - Set to permit function to halt bulk endpoints.
+                               Disabled on some USB devices known not to work
+                               correctly. You should set it to true.
+               num_buffers     - Number of pipeline buffers. Valid numbers
+                               are 2..4. Available only if
+                               CONFIG_USB_GADGET_DEBUG_FILES is set.
+
+What:          /config/usb-gadget/gadget/functions/mass_storage.name/lun.name
+Date:          Oct 2013
+KenelVersion:  3.13
+Description:
+               The attributes:
+
+               file            - The path to the backing file for the LUN.
+                               Required if LUN is not marked as removable.
+               ro              - Flag specifying access to the LUN shall be
+                               read-only. This is implied if CD-ROM emulation
+                               is enabled as well as when it was impossible
+                               to open "filename" in R/W mode.
+               removable       - Flag specifying that LUN shall be indicated as
+                               being removable.
+               cdrom           - Flag specifying that LUN shall be reported as
+                               being a CD-ROM.
+               nofua           - Flag specifying that FUA flag
+                               in SCSI WRITE(10,12)
diff --git a/Documentation/ABI/testing/sysfs-class-mic.txt b/Documentation/ABI/testing/sysfs-class-mic.txt
new file mode 100644 (file)
index 0000000..13f48af
--- /dev/null
@@ -0,0 +1,157 @@
+What:          /sys/class/mic/
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               The mic class directory belongs to Intel MIC devices and
+               provides information per MIC device. An Intel MIC device is a
+               PCIe form factor add-in Coprocessor card based on the Intel Many
+               Integrated Core (MIC) architecture that runs a Linux OS.
+
+What:          /sys/class/mic/mic(x)
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               The directories /sys/class/mic/mic0, /sys/class/mic/mic1 etc.,
+               represent MIC devices (0,1,..etc). Each directory has
+               information specific to that MIC device.
+
+What:          /sys/class/mic/mic(x)/family
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               Provides information about the Coprocessor family for an Intel
+               MIC device. For example - "x100"
+
+What:          /sys/class/mic/mic(x)/stepping
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               Provides information about the silicon stepping for an Intel
+               MIC device. For example - "A0" or "B0"
+
+What:          /sys/class/mic/mic(x)/state
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               When read, this entry provides the current state of an Intel
+               MIC device in the context of the card OS. Possible values that
+               will be read are:
+               "offline" - The MIC device is ready to boot the card OS. On
+               reading this entry after an OSPM resume, a "boot" has to be
+               written to this entry if the card was previously shutdown
+               during OSPM suspend.
+               "online" - The MIC device has initiated booting a card OS.
+               "shutting_down" - The card OS is shutting down.
+               "reset_failed" - The MIC device has failed to reset.
+               "suspending" - The MIC device is currently being prepared for
+               suspend. On reading this entry, a "suspend" has to be written
+               to the state sysfs entry to ensure the card is shutdown during
+               OSPM suspend.
+               "suspended" - The MIC device has been suspended.
+
+               When written, this sysfs entry triggers different state change
+               operations depending upon the current state of the card OS.
+               Acceptable values are:
+               "boot" - Boot the card OS image specified by the combination
+                        of firmware, ramdisk, cmdline and bootmode
+                       sysfs entries.
+               "reset" - Initiates device reset.
+               "shutdown" - Initiates card OS shutdown.
+               "suspend" - Initiates card OS shutdown and also marks the card
+               as suspended.
+
+What:          /sys/class/mic/mic(x)/shutdown_status
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               An Intel MIC device runs a Linux OS during its operation. This
+               OS can shutdown because of various reasons. When read, this
+               entry provides the status on why the card OS was shutdown.
+               Possible values are:
+               "nop" -  shutdown status is not applicable, when the card OS is
+                       "online"
+               "crashed" - Shutdown because of a HW or SW crash.
+               "halted" - Shutdown because of a halt command.
+               "poweroff" - Shutdown because of a poweroff command.
+               "restart" - Shutdown because of a restart command.
+
+What:          /sys/class/mic/mic(x)/cmdline
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               An Intel MIC device runs a Linux OS during its operation. Before
+               booting this card OS, it is possible to pass kernel command line
+               options to configure various features in it, similar to
+               self-bootable machines. When read, this entry provides
+               information about the current kernel command line options set to
+               boot the card OS. This entry can be written to change the
+               existing kernel command line options. Typically, the user would
+               want to read the current command line options, append new ones
+               or modify existing ones and then write the whole kernel command
+               line back to this entry.
+
+What:          /sys/class/mic/mic(x)/firmware
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               When read, this sysfs entry provides the path name under
+               /lib/firmware/ where the firmware image to be booted on the
+               card can be found. The entry can be written to change the
+               firmware image location under /lib/firmware/.
+
+What:          /sys/class/mic/mic(x)/ramdisk
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               When read, this sysfs entry provides the path name under
+               /lib/firmware/ where the ramdisk image to be used during card
+               OS boot can be found. The entry can be written to change
+               the ramdisk image location under /lib/firmware/.
+
+What:          /sys/class/mic/mic(x)/bootmode
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               When read, this sysfs entry provides the current bootmode for
+               the card. This sysfs entry can be written with the following
+               valid strings:
+               a) linux - Boot a Linux image.
+               b) elf - Boot an elf image for flash updates.
+
+What:          /sys/class/mic/mic(x)/log_buf_addr
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               An Intel MIC device runs a Linux OS during its operation. For
+               debugging purpose and early kernel boot messages, the user can
+               access the card OS log buffer via debugfs. When read, this entry
+               provides the kernel virtual address of the buffer where the card
+               OS log buffer can be read. This entry is written by the host
+               configuration daemon to set the log buffer address. The correct
+               log buffer address to be written can be found in the System.map
+               file of the card OS.
+
+What:          /sys/class/mic/mic(x)/log_buf_len
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               An Intel MIC device runs a Linux OS during its operation. For
+               debugging purpose and early kernel boot messages, the user can
+               access the card OS log buffer via debugfs. When read, this entry
+               provides the kernel virtual address where the card OS log buffer
+               length can be read. This entry is written by host configuration
+               daemon to set the log buffer length address. The correct log
+               buffer length address to be written can be found in the
+               System.map file of the card OS.
diff --git a/Documentation/ABI/testing/sysfs-driver-sunxi-sid b/Documentation/ABI/testing/sysfs-driver-sunxi-sid
new file mode 100644 (file)
index 0000000..ffb9536
--- /dev/null
@@ -0,0 +1,22 @@
+What:          /sys/devices/*/<our-device>/eeprom
+Date:          August 2013
+Contact:       Oliver Schinagl <oliver@schinagl.nl>
+Description:   read-only access to the SID (Security-ID) on current
+               A-series SoC's from Allwinner. Currently supports A10, A10s, A13
+               and A20 CPU's. The earlier A1x series of SoCs exports 16 bytes,
+               whereas the newer A20 SoC exposes 512 bytes split into sections.
+               Besides the 16 bytes of SID, there's also an SJTAG area,
+               HDMI-HDCP key and some custom keys. Below a quick overview, for
+               details see the user manual:
+               0x000  128 bit root-key (sun[457]i)
+               0x010  128 bit boot-key (sun7i)
+               0x020   64 bit security-jtag-key (sun7i)
+               0x028   16 bit key configuration (sun7i)
+               0x02b   16 bit custom-vendor-key (sun7i)
+               0x02c  320 bit low general key (sun7i)
+               0x040   32 bit read-control access (sun7i)
+               0x064  224 bit low general key (sun7i)
+               0x080 2304 bit HDCP-key (sun7i)
+               0x1a0  768 bit high general key (sun7i)
+Users:         any user space application which wants to read the SID on
+               Allwinner's A-series of CPU's.
index 25b58efd955dd2bebf2d716aea851fe283f792c1..4f676838da06a61aed3e26243185a0a7fa2f0311 100644 (file)
@@ -91,7 +91,6 @@
      <title>The Filesystem for Exporting Kernel Objects</title>
 !Efs/sysfs/file.c
 !Efs/sysfs/symlink.c
-!Efs/sysfs/bin.c
   </chapter>
 
   <chapter id="debugfs">
index 4848db8c71ff5118244888b620251f49831c92f2..8a4da64e02a8b08f3ad7ed6359ef713d7afd3b54 100644 (file)
@@ -71,7 +71,7 @@ static int netlink_send(int s, struct cn_msg *msg)
        nlh->nlmsg_seq = seq++;
        nlh->nlmsg_pid = getpid();
        nlh->nlmsg_type = NLMSG_DONE;
-       nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
+       nlh->nlmsg_len = size;
        nlh->nlmsg_flags = 0;
 
        m = NLMSG_DATA(nlh);
diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
new file mode 100644 (file)
index 0000000..68ba372
--- /dev/null
@@ -0,0 +1,17 @@
+Allwinner sunxi-sid
+
+Required properties:
+- compatible: "allwinner,sun4i-sid" or "allwinner,sun7i-a20-sid".
+- reg: Should contain registers location and length
+
+Example for sun4i:
+       sid@01c23800 {
+               compatible = "allwinner,sun4i-sid";
+               reg = <0x01c23800 0x10>
+       };
+
+Example for sun7i:
+       sid@01c23800 {
+               compatible = "allwinner,sun7i-a20-sid";
+               reg = <0x01c23800 0x200>
+       };
diff --git a/Documentation/devicetree/bindings/misc/ti,dac7512.txt b/Documentation/devicetree/bindings/misc/ti,dac7512.txt
new file mode 100644 (file)
index 0000000..1db4593
--- /dev/null
@@ -0,0 +1,20 @@
+TI DAC7512 DEVICETREE BINDINGS
+
+Required properties:
+
+       - "compatible"          Must be set to "ti,dac7512"
+
+Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt
+apply. In particular, "reg" and "spi-max-frequency" properties must be given.
+
+
+Example:
+
+       spi_master {
+               dac7512: dac7512@0 {
+                       compatible = "ti,dac7512";
+                       reg = <0>; /* CS0 */
+                       spi-max-frequency = <1000000>;
+               };
+       };
+
diff --git a/Documentation/devicetree/bindings/phy/phy-bindings.txt b/Documentation/devicetree/bindings/phy/phy-bindings.txt
new file mode 100644 (file)
index 0000000..8ae844f
--- /dev/null
@@ -0,0 +1,66 @@
+This document explains only the device tree data binding. For general
+information about PHY subsystem refer to Documentation/phy.txt
+
+PHY device node
+===============
+
+Required Properties:
+#phy-cells:    Number of cells in a PHY specifier;  The meaning of all those
+               cells is defined by the binding for the phy node. The PHY
+               provider can use the values in cells to find the appropriate
+               PHY.
+
+For example:
+
+phys: phy {
+    compatible = "xxx";
+    reg = <...>;
+    .
+    .
+    #phy-cells = <1>;
+    .
+    .
+};
+
+That node describes an IP block (PHY provider) that implements 2 different PHYs.
+In order to differentiate between these 2 PHYs, an additonal specifier should be
+given while trying to get a reference to it.
+
+PHY user node
+=============
+
+Required Properties:
+phys : the phandle for the PHY device (used by the PHY subsystem)
+phy-names : the names of the PHY corresponding to the PHYs present in the
+           *phys* phandle
+
+Example 1:
+usb1: usb_otg_ss@xxx {
+    compatible = "xxx";
+    reg = <xxx>;
+    .
+    .
+    phys = <&usb2_phy>, <&usb3_phy>;
+    phy-names = "usb2phy", "usb3phy";
+    .
+    .
+};
+
+This node represents a controller that uses two PHYs, one for usb2 and one for
+usb3.
+
+Example 2:
+usb2: usb_otg_ss@xxx {
+    compatible = "xxx";
+    reg = <xxx>;
+    .
+    .
+    phys = <&phys 1>;
+    phy-names = "usbphy";
+    .
+    .
+};
+
+This node represents a controller that uses one of the PHYs of the PHY provider
+device defined previously. Note that the phy handle has an additional specifier
+"1" to differentiate between the two PHYs.
diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
new file mode 100644 (file)
index 0000000..c0fccaa
--- /dev/null
@@ -0,0 +1,22 @@
+Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be "samsung,s5pv210-mipi-video-phy";
+- reg : offset and length of the MIPI DPHY register set;
+- #phy-cells : from the generic phy bindings, must be 1;
+
+For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in
+the PHY specifier identifies the PHY and its meaning is as follows:
+  0 - MIPI CSIS 0,
+  1 - MIPI DSIM 0,
+  2 - MIPI CSIS 1,
+  3 - MIPI DSIM 1.
+
+Samsung EXYNOS SoC series Display Port PHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be "samsung,exynos5250-dp-video-phy";
+- reg : offset and length of the Display Port PHY register set;
+- #phy-cells : from the generic PHY bindings, must be 0;
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
new file mode 100644 (file)
index 0000000..5ea26c6
--- /dev/null
@@ -0,0 +1,17 @@
+MSM SoC HSUSB controllers
+
+EHCI
+
+Required properties:
+- compatible:  Should contain "qcom,ehci-host"
+- regs:                        offset and length of the register set in the memory map
+- usb-phy:             phandle for the PHY device
+
+Example EHCI controller device node:
+
+       ehci: ehci@f9a55000 {
+               compatible = "qcom,ehci-host";
+               reg = <0xf9a55000 0x400>;
+               usb-phy = <&usb_otg>;
+       };
+
index 9088ab09e20028c99257244975600fa24f44c403..090e5e22bd2b831a1b05885d5d01db228aec9bef 100644 (file)
@@ -3,9 +3,6 @@ OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
 OMAP MUSB GLUE
  - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
  - ti,hwmods : must be "usb_otg_hs"
- - ti,has-mailbox : to specify that omap uses an external mailbox
-   (in control module) to communicate with the musb core during device connect
-   and disconnect.
  - multipoint : Should be "1" indicating the musb controller supports
    multipoint. This is a MUSB configuration-specific setting.
  - num-eps : Specifies the number of endpoints. This is also a
@@ -19,6 +16,9 @@ OMAP MUSB GLUE
  - power : Should be "50". This signifies the controller can supply up to
    100mA when operating in host mode.
  - usb-phy : the phandle for the PHY device
+ - phys : the phandle for the PHY device (used by generic PHY framework)
+ - phy-names : the names of the PHY corresponding to the PHYs present in the
+   *phy* phandle.
 
 Optional properties:
  - ctrl-module : phandle of the control module this glue uses to write to
@@ -28,11 +28,12 @@ SOC specific device node entry
 usb_otg_hs: usb_otg_hs@4a0ab000 {
        compatible = "ti,omap4-musb";
        ti,hwmods = "usb_otg_hs";
-       ti,has-mailbox;
        multipoint = <1>;
        num-eps = <16>;
        ram-bits = <12>;
        ctrl-module = <&omap_control_usb>;
+       phys = <&usb2_phy>;
+       phy-names = "usb2-phy";
 };
 
 Board specific device node entry
@@ -78,22 +79,22 @@ omap_dwc3 {
 OMAP CONTROL USB
 
 Required properties:
- - compatible: Should be "ti,omap-control-usb"
+ - compatible: Should be one of
+ "ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
+ "ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
+                       e.g. USB2_PHY on OMAP5.
+ "ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
+                       e.g. USB3 PHY and SATA PHY on OMAP5.
+ "ti,control-phy-dra7usb2" - if it has power down register like USB2 PHY on
+                       DRA7 platform.
  - reg : Address and length of the register set for the device. It contains
-   the address of "control_dev_conf" and "otghs_control" or "phy_power_usb"
-   depending upon omap4 or omap5.
- - reg-names: The names of the register addresses corresponding to the registers
-   filled in "reg".
- - ti,type: This is used to differentiate whether the control module has
-   usb mailbox or usb3 phy power. omap4 has usb mailbox in control module to
-   notify events to the musb core and omap5 has usb3 phy power register to
-   power on usb3 phy. Should be "1" if it has mailbox and "2" if it has usb3
-   phy power.
+   the address of "otghs_control" for control-phy-otghs or "power" register
+   for other types.
+ - reg-names: should be "otghs_control" control-phy-otghs and "power" for
+   other types.
 
 omap_control_usb: omap-control-usb@4a002300 {
-       compatible = "ti,omap-control-usb";
-       reg = <0x4a002300 0x4>,
-             <0x4a00233c 0x4>;
-       reg-names = "control_dev_conf", "otghs_control";
-       ti,type = <1>;
+       compatible = "ti,control-phy-otghs";
+       reg = <0x4a00233c 0x4>;
+       reg-names = "otghs_control";
 };
index d7e272671c7e42819c35fc0583d3744aaecd74d9..1bd37faba05b9c3aab70a0ec305107f308ba7665 100644 (file)
@@ -15,7 +15,7 @@ Optional properties:
 
 - vcc-supply: phandle to the regulator that provides RESET to the PHY.
 
-- reset-supply: phandle to the regulator that provides power to the PHY.
+- reset-gpios: Should specify the GPIO for reset.
 
 Example:
 
@@ -25,10 +25,9 @@ Example:
                clocks = <&osc 0>;
                clock-names = "main_clk";
                vcc-supply = <&hsusb1_vcc_regulator>;
-               reset-supply = <&hsusb1_reset_regulator>;
+               reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
        };
 
 hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator
 and expects that clock to be configured to 19.2MHz by the NOP PHY driver.
-hsusb1_vcc_regulator provides power to the PHY and hsusb1_reset_regulator
-controls RESET.
+hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET.
index 61496f5cb095b39c149705e7f80c3fba379c434d..c0245c888982b9be44bad31e76664880e2449e2c 100644 (file)
@@ -5,6 +5,8 @@ OMAP USB2 PHY
 Required properties:
  - compatible: Should be "ti,omap-usb2"
  - reg : Address and length of the register set for the device.
+ - #phy-cells: determine the number of cells that should be given in the
+   phandle while referencing this phy.
 
 Optional properties:
  - ctrl-module : phandle of the control module used by PHY driver to power on
@@ -16,6 +18,7 @@ usb2phy@4a0ad080 {
        compatible = "ti,omap-usb2";
        reg = <0x4a0ad080 0x58>;
        ctrl-module = <&omap_control_usb>;
+       #phy-cells = <0>;
 };
 
 OMAP USB3 PHY
@@ -25,6 +28,8 @@ Required properties:
  - reg : Address and length of the register set for the device.
  - reg-names: The names of the register addresses corresponding to the registers
    filled in "reg".
+ - #phy-cells: determine the number of cells that should be given in the
+   phandle while referencing this phy.
 
 Optional properties:
  - ctrl-module : phandle of the control module used by PHY driver to power on
@@ -39,4 +44,5 @@ usb3phy@4a084400 {
              <0x4a084c00 0x40>;
        reg-names = "phy_rx", "phy_tx", "pll_ctrl";
        ctrl-module = <&omap_control_usb>;
+       #phy-cells = <0>;
 };
index 84f10c16cb383497b0fc5a736d4ce03a59668c4c..3289d76a21d0b2d01b81620e0c19fb10af29cf6b 100644 (file)
@@ -6,10 +6,10 @@ We use two nodes:
        -dptx-phy node(defined inside dp-controller node)
 
 For the DP-PHY initialization, we use the dptx-phy node.
-Required properties for dptx-phy:
-       -reg:
+Required properties for dptx-phy: deprecated, use phys and phy-names
+       -reg: deprecated
                Base address of DP PHY register.
-       -samsung,enable-mask:
+       -samsung,enable-mask: deprecated
                The bit-mask used to enable/disable DP PHY.
 
 For the Panel initialization, we read data from dp-controller node.
@@ -27,6 +27,10 @@ Required properties for dp-controller:
                from common clock binding: Shall be "dp".
        -interrupt-parent:
                phandle to Interrupt combiner node.
+       -phys:
+               from general PHY binding: the phandle for the PHY device.
+       -phy-names:
+               from general PHY binding: Should be "dp".
        -samsung,color-space:
                input video data format.
                        COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2
@@ -68,11 +72,8 @@ SOC specific portion:
                clocks = <&clock 342>;
                clock-names = "dp";
 
-               dptx-phy {
-                       reg = <0x10040720>;
-                       samsung,enable-mask = <1>;
-               };
-
+               phys = <&dp_phy>;
+               phy-names = "dp";
        };
 
 Board Specific portion:
index eb0fa5f4fe88f4b6e6b2b381f41561367bd80be8..5377f63179613e6f09721bf878e52e7f04a141a8 100644 (file)
@@ -25,8 +25,10 @@ MyungJoo Ham <myungjoo.ham@samsung.com>
     @print_state: no change but type change (switch_dev->extcon_dev)
 
 - switch_dev_register(sdev, dev)
-       => extcon_dev_register(edev, dev)
-       : no change but type change (sdev->edev)
+       => extcon_dev_register(edev)
+       : type change (sdev->edev)
+       : remove second param('dev'). if edev has parent device, should store
+         'dev' to 'edev.dev.parent' before registering extcon device
 - switch_dev_unregister(sdev)
        => extcon_dev_unregister(edev)
        : no change but type change (sdev->edev)
diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt
new file mode 100644 (file)
index 0000000..b419292
--- /dev/null
@@ -0,0 +1,51 @@
+An Intel MIC X100 device is a PCIe form factor add-in coprocessor
+card based on the Intel Many Integrated Core (MIC) architecture
+that runs a Linux OS. It is a PCIe endpoint in a platform and therefore
+implements the three required standard address spaces i.e. configuration,
+memory and I/O. The host OS loads a device driver as is typical for
+PCIe devices. The card itself runs a bootstrap after reset that
+transfers control to the card OS downloaded from the host driver. The
+host driver supports OSPM suspend and resume operations. It shuts down
+the card during suspend and reboots the card OS during resume.
+The card OS as shipped by Intel is a Linux kernel with modifications
+for the X100 devices.
+
+Since it is a PCIe card, it does not have the ability to host hardware
+devices for networking, storage and console. We provide these devices
+on X100 coprocessors thus enabling a self-bootable equivalent environment
+for applications. A key benefit of our solution is that it leverages
+the standard virtio framework for network, disk and console devices,
+though in our case the virtio framework is used across a PCIe bus.
+
+Here is a block diagram of the various components described above. The
+virtio backends are situated on the host rather than the card given better
+single threaded performance for the host compared to MIC, the ability of
+the host to initiate DMA's to/from the card using the MIC DMA engine and
+the fact that the virtio block storage backend can only be on the host.
+
+                              |
+       +----------+           |             +----------+
+       | Card OS  |           |             | Host OS  |
+       +----------+           |             +----------+
+                              |
++-------+ +--------+ +------+ | +---------+  +--------+ +--------+
+| Virtio| |Virtio  | |Virtio| | |Virtio   |  |Virtio  | |Virtio  |
+| Net   | |Console | |Block | | |Net      |  |Console | |Block   |
+| Driver| |Driver  | |Driver| | |backend  |  |backend | |backend |
++-------+ +--------+ +------+ | +---------+  +--------+ +--------+
+    |         |         |     |      |            |         |
+    |         |         |     |User  |            |         |
+    |         |         |     |------|------------|---------|-------
+    +-------------------+     |Kernel +--------------------------+
+              |               |       | Virtio over PCIe IOCTLs  |
+              |               |       +--------------------------+
+      +--------------+        |                   |
+      |Intel MIC     |        |            +---------------+
+      |Card Driver   |        |            |Intel MIC      |
+      +--------------+        |            |Host Driver    |
+              |               |            +---------------+
+              |               |                   |
+     +-------------------------------------------------------------+
+     |                                                             |
+     |                    PCIe Bus                                 |
+     +-------------------------------------------------------------+
diff --git a/Documentation/mic/mpssd/.gitignore b/Documentation/mic/mpssd/.gitignore
new file mode 100644 (file)
index 0000000..8b7c72f
--- /dev/null
@@ -0,0 +1 @@
+mpssd
diff --git a/Documentation/mic/mpssd/Makefile b/Documentation/mic/mpssd/Makefile
new file mode 100644 (file)
index 0000000..eb860a7
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# Makefile - Intel MIC User Space Tools.
+# Copyright(c) 2013, Intel Corporation.
+#
+ifdef DEBUG
+CFLAGS += $(USERWARNFLAGS) -I. -g -Wall -DDEBUG=$(DEBUG)
+else
+CFLAGS += $(USERWARNFLAGS) -I. -g -Wall
+endif
+
+mpssd: mpssd.o sysfs.o
+       $(CC) $(CFLAGS) -o $@ $^ -lpthread
+
+install:
+       install mpssd /usr/sbin/mpssd
+       install micctrl /usr/sbin/micctrl
+
+clean:
+       rm -f mpssd *.o
diff --git a/Documentation/mic/mpssd/micctrl b/Documentation/mic/mpssd/micctrl
new file mode 100755 (executable)
index 0000000..8f2629b
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/bash
+# Intel MIC Platform Software Stack (MPSS)
+#
+# Copyright(c) 2013 Intel 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.
+#
+# 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.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
+# Intel MIC User Space Tools.
+#
+# micctrl - Controls MIC boot/start/stop.
+#
+# chkconfig: 2345 95 05
+# description: start MPSS stack processing.
+#
+### BEGIN INIT INFO
+# Provides: micctrl
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+sysfs="/sys/class/mic"
+
+_status()
+{
+       f=$sysfs/$1
+       echo -e $1 state: "`cat $f/state`" shutdown_status: "`cat $f/shutdown_status`"
+}
+
+status()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _status $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _status `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_reset()
+{
+       f=$sysfs/$1
+       echo reset > $f/state
+}
+
+reset()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _reset $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _reset `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_boot()
+{
+       f=$sysfs/$1
+       echo "linux" > $f/bootmode
+       echo "mic/uos.img" > $f/firmware
+       echo "mic/$1.image" > $f/ramdisk
+       echo "boot" > $f/state
+}
+
+boot()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _boot $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _boot `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_shutdown()
+{
+       f=$sysfs/$1
+       echo shutdown > $f/state
+}
+
+shutdown()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _shutdown $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _shutdown `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_wait()
+{
+       f=$sysfs/$1
+       while [ "`cat $f/state`" != "offline" -a "`cat $f/state`" != "online" ]
+       do
+               sleep 1
+               echo -e "Waiting for $1 to go offline"
+       done
+}
+
+wait()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _wait $1
+               return $?
+       fi
+       # Wait for the cards to go offline
+       for f in $sysfs/*
+       do
+               _wait `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+if [ ! -d "$sysfs" ]; then
+       echo -e $"Module unloaded "
+       exit 3
+fi
+
+case $1 in
+       -s)
+               status $2
+               ;;
+       -r)
+               reset $2
+               ;;
+       -b)
+               boot $2
+               ;;
+       -S)
+               shutdown $2
+               ;;
+       -w)
+               wait $2
+               ;;
+       *)
+               echo $"Usage: $0 {-s (status) |-r (reset) |-b (boot) |-S (shutdown) |-w (wait)}"
+               exit 2
+esac
+
+exit $?
diff --git a/Documentation/mic/mpssd/mpss b/Documentation/mic/mpssd/mpss
new file mode 100755 (executable)
index 0000000..3136c68
--- /dev/null
@@ -0,0 +1,202 @@
+#!/bin/bash
+# Intel MIC Platform Software Stack (MPSS)
+#
+# Copyright(c) 2013 Intel 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.
+#
+# 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.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
+# Intel MIC User Space Tools.
+#
+# mpss Start mpssd.
+#
+# chkconfig: 2345 95 05
+# description: start MPSS stack processing.
+#
+### BEGIN INIT INFO
+# Provides: mpss
+# Required-Start:
+# Required-Stop:
+# Short-Description: MPSS stack control
+# Description: MPSS stack control
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+exec=/usr/sbin/mpssd
+sysfs="/sys/class/mic"
+
+start()
+{
+       [ -x $exec ] || exit 5
+
+       if [ "`ps -e | awk '{print $4}' | grep mpssd | head -1`" = "mpssd" ]; then
+               echo -e $"MPSSD already running! "
+               success
+               echo
+               return 0
+       fi
+
+       echo -e $"Starting MPSS Stack"
+       echo -e $"Loading MIC_HOST Module"
+
+       # Ensure the driver is loaded
+       if [ ! -d "$sysfs" ]; then
+               modprobe mic_host
+               RETVAL=$?
+               if [ $RETVAL -ne 0 ]; then
+                       failure
+                       echo
+                       return $RETVAL
+               fi
+       fi
+
+       # Start the daemon
+       echo -n $"Starting MPSSD "
+       $exec
+       RETVAL=$?
+       if [ $RETVAL -ne 0 ]; then
+               failure
+               echo
+               return $RETVAL
+       fi
+       success
+       echo
+
+       sleep 5
+
+       # Boot the cards
+       micctrl -b
+
+       # Wait till ping works
+       for f in $sysfs/*
+       do
+               count=100
+               ipaddr=`cat $f/cmdline`
+               ipaddr=${ipaddr#*address,}
+               ipaddr=`echo $ipaddr | cut -d, -f1 | cut -d\; -f1`
+               while [ $count -ge 0 ]
+               do
+                       echo -e "Pinging "`basename $f`" "
+                       ping -c 1 $ipaddr &> /dev/null
+                       RETVAL=$?
+                       if [ $RETVAL -eq 0 ]; then
+                               success
+                               break
+                       fi
+                       sleep 1
+                       count=`expr $count - 1`
+               done
+               [ $RETVAL -ne 0 ] && failure || success
+               echo
+       done
+       return $RETVAL
+}
+
+stop()
+{
+       echo -e $"Shutting down MPSS Stack: "
+
+       # Bail out if module is unloaded
+       if [ ! -d "$sysfs" ]; then
+               echo -n $"Module unloaded "
+               success
+               echo
+               return 0
+       fi
+
+       # Shut down the cards.
+       micctrl -S
+
+       # Wait for the cards to go offline
+       for f in $sysfs/*
+       do
+               while [ "`cat $f/state`" != "offline" ]
+               do
+                       sleep 1
+                       echo -e "Waiting for "`basename $f`" to go offline"
+               done
+       done
+
+       # Display the status of the cards
+       micctrl -s
+
+       # Kill MPSSD now
+       echo -n $"Killing MPSSD"
+       killall -9 mpssd 2>/dev/null
+       RETVAL=$?
+       [ $RETVAL -ne 0 ] && failure || success
+       echo
+       return $RETVAL
+}
+
+restart()
+{
+       stop
+       sleep 5
+       start
+}
+
+status()
+{
+       micctrl -s
+       if [ "`ps -e | awk '{print $4}' | grep mpssd | head -n 1`" = "mpssd" ]; then
+               echo "mpssd is running"
+       else
+               echo "mpssd is stopped"
+       fi
+       return 0
+}
+
+unload()
+{
+       if [ ! -d "$sysfs" ]; then
+               echo -n $"No MIC_HOST Module: "
+               success
+               echo
+               return
+       fi
+
+       stop
+
+       sleep 5
+       echo -n $"Removing MIC_HOST Module: "
+       modprobe -r mic_host
+       RETVAL=$?
+       [ $RETVAL -ne 0 ] && failure || success
+       echo
+       return $RETVAL
+}
+
+case $1 in
+       start)
+               start
+               ;;
+       stop)
+               stop
+               ;;
+       restart)
+               restart
+               ;;
+       status)
+               status
+               ;;
+       unload)
+               unload
+               ;;
+       *)
+               echo $"Usage: $0 {start|stop|restart|status|unload}"
+               exit 2
+esac
+
+exit $?
diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c
new file mode 100644 (file)
index 0000000..0c980ad
--- /dev/null
@@ -0,0 +1,1721 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <poll.h>
+#include <features.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_net.h>
+#include <linux/virtio_console.h>
+#include <linux/virtio_blk.h>
+#include <linux/version.h>
+#include "mpssd.h"
+#include <linux/mic_ioctl.h>
+#include <linux/mic_common.h>
+
+static void init_mic(struct mic_info *mic);
+
+static FILE *logfp;
+static struct mic_info mic_list;
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define min_t(type, x, y) ({                           \
+               type __min1 = (x);                      \
+               type __min2 = (y);                      \
+               __min1 < __min2 ? __min1 : __min2; })
+
+/* align addr on a size boundary - adjust address up/down if needed */
+#define _ALIGN_DOWN(addr, size)  ((addr)&(~((size)-1)))
+#define _ALIGN_UP(addr, size)    _ALIGN_DOWN(addr + size - 1, size)
+
+/* align addr on a size boundary - adjust address up if needed */
+#define _ALIGN(addr, size)     _ALIGN_UP(addr, size)
+
+/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr)        _ALIGN(addr, PAGE_SIZE)
+
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
+
+#define GSO_ENABLED            1
+#define MAX_GSO_SIZE           (64 * 1024)
+#define ETH_H_LEN              14
+#define MAX_NET_PKT_SIZE       (_ALIGN_UP(MAX_GSO_SIZE + ETH_H_LEN, 64))
+#define MIC_DEVICE_PAGE_END    0x1000
+
+#ifndef VIRTIO_NET_HDR_F_DATA_VALID
+#define VIRTIO_NET_HDR_F_DATA_VALID    2       /* Csum is valid */
+#endif
+
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[2];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_console_config cons_config;
+} virtcons_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_CONSOLE,
+               .num_vq = ARRAY_SIZE(virtcons_dev_page.vqconfig),
+               .feature_len = sizeof(virtcons_dev_page.host_features),
+               .config_len = sizeof(virtcons_dev_page.cons_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .vqconfig[1] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+};
+
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[2];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_net_config net_config;
+} virtnet_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_NET,
+               .num_vq = ARRAY_SIZE(virtnet_dev_page.vqconfig),
+               .feature_len = sizeof(virtnet_dev_page.host_features),
+               .config_len = sizeof(virtnet_dev_page.net_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .vqconfig[1] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+#if GSO_ENABLED
+               .host_features = htole32(
+               1 << VIRTIO_NET_F_CSUM |
+               1 << VIRTIO_NET_F_GSO |
+               1 << VIRTIO_NET_F_GUEST_TSO4 |
+               1 << VIRTIO_NET_F_GUEST_TSO6 |
+               1 << VIRTIO_NET_F_GUEST_ECN |
+               1 << VIRTIO_NET_F_GUEST_UFO),
+#else
+               .host_features = 0,
+#endif
+};
+
+static const char *mic_config_dir = "/etc/sysconfig/mic";
+static const char *virtblk_backend = "VIRTBLK_BACKEND";
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[1];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_blk_config blk_config;
+} virtblk_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_BLOCK,
+               .num_vq = ARRAY_SIZE(virtblk_dev_page.vqconfig),
+               .feature_len = sizeof(virtblk_dev_page.host_features),
+               .config_len = sizeof(virtblk_dev_page.blk_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .host_features =
+               htole32(1<<VIRTIO_BLK_F_SEG_MAX),
+       .blk_config = {
+               .seg_max = htole32(MIC_VRING_ENTRIES - 2),
+               .capacity = htole64(0),
+        }
+};
+
+static char *myname;
+
+static int
+tap_configure(struct mic_info *mic, char *dev)
+{
+       pid_t pid;
+       char *ifargv[7];
+       char ipaddr[IFNAMSIZ];
+       int ret = 0;
+
+       pid = fork();
+       if (pid == 0) {
+               ifargv[0] = "ip";
+               ifargv[1] = "link";
+               ifargv[2] = "set";
+               ifargv[3] = dev;
+               ifargv[4] = "up";
+               ifargv[5] = NULL;
+               mpsslog("Configuring %s\n", dev);
+               ret = execvp("ip", ifargv);
+               if (ret < 0) {
+                       mpsslog("%s execvp failed errno %s\n",
+                               mic->name, strerror(errno));
+                       return ret;
+               }
+       }
+       if (pid < 0) {
+               mpsslog("%s fork failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       ret = waitpid(pid, NULL, 0);
+       if (ret < 0) {
+               mpsslog("%s waitpid failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id);
+
+       pid = fork();
+       if (pid == 0) {
+               ifargv[0] = "ip";
+               ifargv[1] = "addr";
+               ifargv[2] = "add";
+               ifargv[3] = ipaddr;
+               ifargv[4] = "dev";
+               ifargv[5] = dev;
+               ifargv[6] = NULL;
+               mpsslog("Configuring %s ipaddr %s\n", dev, ipaddr);
+               ret = execvp("ip", ifargv);
+               if (ret < 0) {
+                       mpsslog("%s execvp failed errno %s\n",
+                               mic->name, strerror(errno));
+                       return ret;
+               }
+       }
+       if (pid < 0) {
+               mpsslog("%s fork failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       ret = waitpid(pid, NULL, 0);
+       if (ret < 0) {
+               mpsslog("%s waitpid failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+       mpsslog("MIC name %s %s %d DONE!\n",
+               mic->name, __func__, __LINE__);
+       return 0;
+}
+
+static int tun_alloc(struct mic_info *mic, char *dev)
+{
+       struct ifreq ifr;
+       int fd, err;
+#if GSO_ENABLED
+       unsigned offload;
+#endif
+       fd = open("/dev/net/tun", O_RDWR);
+       if (fd < 0) {
+               mpsslog("Could not open /dev/net/tun %s\n", strerror(errno));
+               goto done;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+
+       ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
+       if (*dev)
+               strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+       err = ioctl(fd, TUNSETIFF, (void *)&ifr);
+       if (err < 0) {
+               mpsslog("%s %s %d TUNSETIFF failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               close(fd);
+               return err;
+       }
+#if GSO_ENABLED
+       offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
+               TUN_F_TSO_ECN | TUN_F_UFO;
+
+       err = ioctl(fd, TUNSETOFFLOAD, offload);
+       if (err < 0) {
+               mpsslog("%s %s %d TUNSETOFFLOAD failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               close(fd);
+               return err;
+       }
+#endif
+       strcpy(dev, ifr.ifr_name);
+       mpsslog("Created TAP %s\n", dev);
+done:
+       return fd;
+}
+
+#define NET_FD_VIRTIO_NET 0
+#define NET_FD_TUN 1
+#define MAX_NET_FD 2
+
+static void set_dp(struct mic_info *mic, int type, void *dp)
+{
+       switch (type) {
+       case VIRTIO_ID_CONSOLE:
+               mic->mic_console.console_dp = dp;
+               return;
+       case VIRTIO_ID_NET:
+               mic->mic_net.net_dp = dp;
+               return;
+       case VIRTIO_ID_BLOCK:
+               mic->mic_virtblk.block_dp = dp;
+               return;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       assert(0);
+}
+
+static void *get_dp(struct mic_info *mic, int type)
+{
+       switch (type) {
+       case VIRTIO_ID_CONSOLE:
+               return mic->mic_console.console_dp;
+       case VIRTIO_ID_NET:
+               return mic->mic_net.net_dp;
+       case VIRTIO_ID_BLOCK:
+               return mic->mic_virtblk.block_dp;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       assert(0);
+       return NULL;
+}
+
+static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type)
+{
+       struct mic_device_desc *d;
+       int i;
+       void *dp = get_dp(mic, type);
+
+       for (i = mic_aligned_size(struct mic_bootparam); i < PAGE_SIZE;
+               i += mic_total_desc_size(d)) {
+               d = dp + i;
+
+               /* End of list */
+               if (d->type == 0)
+                       break;
+
+               if (d->type == -1)
+                       continue;
+
+               mpsslog("%s %s d-> type %d d %p\n",
+                       mic->name, __func__, d->type, d);
+
+               if (d->type == (__u8)type)
+                       return d;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       assert(0);
+       return NULL;
+}
+
+/* See comments in vhost.c for explanation of next_desc() */
+static unsigned next_desc(struct vring_desc *desc)
+{
+       unsigned int next;
+
+       if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT))
+               return -1U;
+       next = le16toh(desc->next);
+       return next;
+}
+
+/* Sum up all the IOVEC length */
+static ssize_t
+sum_iovec_len(struct mic_copy_desc *copy)
+{
+       ssize_t sum = 0;
+       int i;
+
+       for (i = 0; i < copy->iovcnt; i++)
+               sum += copy->iov[i].iov_len;
+       return sum;
+}
+
+static inline void verify_out_len(struct mic_info *mic,
+       struct mic_copy_desc *copy)
+{
+       if (copy->out_len != sum_iovec_len(copy)) {
+               mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%zx\n",
+                       mic->name, __func__, __LINE__,
+                       copy->out_len, sum_iovec_len(copy));
+               assert(copy->out_len == sum_iovec_len(copy));
+       }
+}
+
+/* Display an iovec */
+static void
+disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy,
+          const char *s, int line)
+{
+       int i;
+
+       for (i = 0; i < copy->iovcnt; i++)
+               mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%zx\n",
+                       mic->name, s, line, i,
+                       copy->iov[i].iov_base, copy->iov[i].iov_len);
+}
+
+static inline __u16 read_avail_idx(struct mic_vring *vr)
+{
+       return ACCESS_ONCE(vr->info->avail_idx);
+}
+
+static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr,
+                               struct mic_copy_desc *copy, ssize_t len)
+{
+       copy->vr_idx = tx ? 0 : 1;
+       copy->update_used = true;
+       if (type == VIRTIO_ID_NET)
+               copy->iov[1].iov_len = len - sizeof(struct virtio_net_hdr);
+       else
+               copy->iov[0].iov_len = len;
+}
+
+/* Central API which triggers the copies */
+static int
+mic_virtio_copy(struct mic_info *mic, int fd,
+               struct mic_vring *vr, struct mic_copy_desc *copy)
+{
+       int ret;
+
+       ret = ioctl(fd, MIC_VIRTIO_COPY_DESC, copy);
+       if (ret) {
+               mpsslog("%s %s %d errno %s ret %d\n",
+                       mic->name, __func__, __LINE__,
+                       strerror(errno), ret);
+       }
+       return ret;
+}
+
+/*
+ * This initialization routine requires at least one
+ * vring i.e. vr0. vr1 is optional.
+ */
+static void *
+init_vr(struct mic_info *mic, int fd, int type,
+       struct mic_vring *vr0, struct mic_vring *vr1, int num_vq)
+{
+       int vr_size;
+       char *va;
+
+       vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES,
+               MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info));
+       va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq,
+               PROT_READ, MAP_SHARED, fd, 0);
+       if (MAP_FAILED == va) {
+               mpsslog("%s %s %d mmap failed errno %s\n",
+                       mic->name, __func__, __LINE__,
+                       strerror(errno));
+               goto done;
+       }
+       set_dp(mic, type, va);
+       vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END];
+       vr0->info = vr0->va +
+               vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN);
+       vring_init(&vr0->vr,
+                  MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN);
+       mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ",
+               __func__, mic->name, vr0->va, vr0->info, vr_size,
+               vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
+       mpsslog("magic 0x%x expected 0x%x\n",
+               vr0->info->magic, MIC_MAGIC + type);
+       assert(vr0->info->magic == MIC_MAGIC + type);
+       if (vr1) {
+               vr1->va = (struct mic_vring *)
+                       &va[MIC_DEVICE_PAGE_END + vr_size];
+               vr1->info = vr1->va + vring_size(MIC_VRING_ENTRIES,
+                       MIC_VIRTIO_RING_ALIGN);
+               vring_init(&vr1->vr,
+                          MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN);
+               mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ",
+                       __func__, mic->name, vr1->va, vr1->info, vr_size,
+                       vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
+               mpsslog("magic 0x%x expected 0x%x\n",
+                       vr1->info->magic, MIC_MAGIC + type + 1);
+               assert(vr1->info->magic == MIC_MAGIC + type + 1);
+       }
+done:
+       return va;
+}
+
+static void
+wait_for_card_driver(struct mic_info *mic, int fd, int type)
+{
+       struct pollfd pollfd;
+       int err;
+       struct mic_device_desc *desc = get_device_desc(mic, type);
+
+       pollfd.fd = fd;
+       mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n",
+               mic->name, __func__, type, desc->status);
+       while (1) {
+               pollfd.events = POLLIN;
+               pollfd.revents = 0;
+               err = poll(&pollfd, 1, -1);
+               if (err < 0) {
+                       mpsslog("%s %s poll failed %s\n",
+                               mic->name, __func__, strerror(errno));
+                       continue;
+               }
+
+               if (pollfd.revents) {
+                       mpsslog("%s %s Waiting... desc-> type %d status 0x%x\n",
+                               mic->name, __func__, type, desc->status);
+                       if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
+                               mpsslog("%s %s poll.revents %d\n",
+                                       mic->name, __func__, pollfd.revents);
+                               mpsslog("%s %s desc-> type %d status 0x%x\n",
+                                       mic->name, __func__, type,
+                                       desc->status);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Spin till we have some descriptors */
+static void
+spin_for_descriptors(struct mic_info *mic, struct mic_vring *vr)
+{
+       __u16 avail_idx = read_avail_idx(vr);
+
+       while (avail_idx == le16toh(ACCESS_ONCE(vr->vr.avail->idx))) {
+#ifdef DEBUG
+               mpsslog("%s %s waiting for desc avail %d info_avail %d\n",
+                       mic->name, __func__,
+                       le16toh(vr->vr.avail->idx), vr->info->avail_idx);
+#endif
+               sched_yield();
+       }
+}
+
+static void *
+virtio_net(void *arg)
+{
+       static __u8 vnet_hdr[2][sizeof(struct virtio_net_hdr)];
+       static __u8 vnet_buf[2][MAX_NET_PKT_SIZE] __aligned(64);
+       struct iovec vnet_iov[2][2] = {
+               { { .iov_base = vnet_hdr[0], .iov_len = sizeof(vnet_hdr[0]) },
+                 { .iov_base = vnet_buf[0], .iov_len = sizeof(vnet_buf[0]) } },
+               { { .iov_base = vnet_hdr[1], .iov_len = sizeof(vnet_hdr[1]) },
+                 { .iov_base = vnet_buf[1], .iov_len = sizeof(vnet_buf[1]) } },
+       };
+       struct iovec *iov0 = vnet_iov[0], *iov1 = vnet_iov[1];
+       struct mic_info *mic = (struct mic_info *)arg;
+       char if_name[IFNAMSIZ];
+       struct pollfd net_poll[MAX_NET_FD];
+       struct mic_vring tx_vr, rx_vr;
+       struct mic_copy_desc copy;
+       struct mic_device_desc *desc;
+       int err;
+
+       snprintf(if_name, IFNAMSIZ, "mic%d", mic->id);
+       mic->mic_net.tap_fd = tun_alloc(mic, if_name);
+       if (mic->mic_net.tap_fd < 0)
+               goto done;
+
+       if (tap_configure(mic, if_name))
+               goto done;
+       mpsslog("MIC name %s id %d\n", mic->name, mic->id);
+
+       net_poll[NET_FD_VIRTIO_NET].fd = mic->mic_net.virtio_net_fd;
+       net_poll[NET_FD_VIRTIO_NET].events = POLLIN;
+       net_poll[NET_FD_TUN].fd = mic->mic_net.tap_fd;
+       net_poll[NET_FD_TUN].events = POLLIN;
+
+       if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd,
+                                 VIRTIO_ID_NET, &tx_vr, &rx_vr,
+               virtnet_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               goto done;
+       }
+
+       copy.iovcnt = 2;
+       desc = get_device_desc(mic, VIRTIO_ID_NET);
+
+       while (1) {
+               ssize_t len;
+
+               net_poll[NET_FD_VIRTIO_NET].revents = 0;
+               net_poll[NET_FD_TUN].revents = 0;
+
+               /* Start polling for data from tap and virtio net */
+               err = poll(net_poll, 2, -1);
+               if (err < 0) {
+                       mpsslog("%s poll failed %s\n",
+                               __func__, strerror(errno));
+                       continue;
+               }
+               if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK))
+                       wait_for_card_driver(mic, mic->mic_net.virtio_net_fd,
+                                            VIRTIO_ID_NET);
+               /*
+                * Check if there is data to be read from TUN and write to
+                * virtio net fd if there is.
+                */
+               if (net_poll[NET_FD_TUN].revents & POLLIN) {
+                       copy.iov = iov0;
+                       len = readv(net_poll[NET_FD_TUN].fd,
+                               copy.iov, copy.iovcnt);
+                       if (len > 0) {
+                               struct virtio_net_hdr *hdr
+                                       = (struct virtio_net_hdr *)vnet_hdr[0];
+
+                               /* Disable checksums on the card since we are on
+                                  a reliable PCIe link */
+                               hdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID;
+#ifdef DEBUG
+                               mpsslog("%s %s %d hdr->flags 0x%x ", mic->name,
+                                       __func__, __LINE__, hdr->flags);
+                               mpsslog("copy.out_len %d hdr->gso_type 0x%x\n",
+                                       copy.out_len, hdr->gso_type);
+#endif
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read from tap 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       len);
+#endif
+                               spin_for_descriptors(mic, &tx_vr);
+                               txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, &copy,
+                                            len);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_net.virtio_net_fd, &tx_vr,
+                                       &copy);
+                               if (err < 0) {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                               }
+                               if (!err)
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d wrote to net 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       sum_iovec_len(&copy));
+#endif
+                               /* Reinitialize IOV for next run */
+                               iov0[1].iov_len = MAX_NET_PKT_SIZE;
+                       } else if (len < 0) {
+                               disp_iovec(mic, &copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read failed %s ", mic->name,
+                                       __func__, __LINE__, strerror(errno));
+                               mpsslog("cnt %d sum %zd\n",
+                                       copy.iovcnt, sum_iovec_len(&copy));
+                       }
+               }
+
+               /*
+                * Check if there is data to be read from virtio net and
+                * write to TUN if there is.
+                */
+               if (net_poll[NET_FD_VIRTIO_NET].revents & POLLIN) {
+                       while (rx_vr.info->avail_idx !=
+                               le16toh(rx_vr.vr.avail->idx)) {
+                               copy.iov = iov1;
+                               txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, &copy,
+                                            MAX_NET_PKT_SIZE
+                                       + sizeof(struct virtio_net_hdr));
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_net.virtio_net_fd, &rx_vr,
+                                       &copy);
+                               if (!err) {
+#ifdef DEBUG
+                                       struct virtio_net_hdr *hdr
+                                               = (struct virtio_net_hdr *)
+                                                       vnet_hdr[1];
+
+                                       mpsslog("%s %s %d hdr->flags 0x%x, ",
+                                               mic->name, __func__, __LINE__,
+                                               hdr->flags);
+                                       mpsslog("out_len %d gso_type 0x%x\n",
+                                               copy.out_len,
+                                               hdr->gso_type);
+#endif
+                                       /* Set the correct output iov_len */
+                                       iov1[1].iov_len = copy.out_len -
+                                               sizeof(struct virtio_net_hdr);
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                                       disp_iovec(mic, copy, __func__,
+                                                  __LINE__);
+                                       mpsslog("%s %s %d ",
+                                               mic->name, __func__, __LINE__);
+                                       mpsslog("read from net 0x%lx\n",
+                                               sum_iovec_len(copy));
+#endif
+                                       len = writev(net_poll[NET_FD_TUN].fd,
+                                               copy.iov, copy.iovcnt);
+                                       if (len != sum_iovec_len(&copy)) {
+                                               mpsslog("Tun write failed %s ",
+                                                       strerror(errno));
+                                               mpsslog("len 0x%zx ", len);
+                                               mpsslog("read_len 0x%zx\n",
+                                                       sum_iovec_len(&copy));
+                                       } else {
+#ifdef DEBUG
+                                               disp_iovec(mic, &copy, __func__,
+                                                          __LINE__);
+                                               mpsslog("%s %s %d ",
+                                                       mic->name, __func__,
+                                                       __LINE__);
+                                               mpsslog("wrote to tap 0x%lx\n",
+                                                       len);
+#endif
+                                       }
+                               } else {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                                       break;
+                               }
+                       }
+               }
+               if (net_poll[NET_FD_VIRTIO_NET].revents & POLLERR)
+                       mpsslog("%s: %s: POLLERR\n", __func__, mic->name);
+       }
+done:
+       pthread_exit(NULL);
+}
+
+/* virtio_console */
+#define VIRTIO_CONSOLE_FD 0
+#define MONITOR_FD (VIRTIO_CONSOLE_FD + 1)
+#define MAX_CONSOLE_FD (MONITOR_FD + 1)  /* must be the last one + 1 */
+#define MAX_BUFFER_SIZE PAGE_SIZE
+
+static void *
+virtio_console(void *arg)
+{
+       static __u8 vcons_buf[2][PAGE_SIZE];
+       struct iovec vcons_iov[2] = {
+               { .iov_base = vcons_buf[0], .iov_len = sizeof(vcons_buf[0]) },
+               { .iov_base = vcons_buf[1], .iov_len = sizeof(vcons_buf[1]) },
+       };
+       struct iovec *iov0 = &vcons_iov[0], *iov1 = &vcons_iov[1];
+       struct mic_info *mic = (struct mic_info *)arg;
+       int err;
+       struct pollfd console_poll[MAX_CONSOLE_FD];
+       int pty_fd;
+       char *pts_name;
+       ssize_t len;
+       struct mic_vring tx_vr, rx_vr;
+       struct mic_copy_desc copy;
+       struct mic_device_desc *desc;
+
+       pty_fd = posix_openpt(O_RDWR);
+       if (pty_fd < 0) {
+               mpsslog("can't open a pseudoterminal master device: %s\n",
+                       strerror(errno));
+               goto _return;
+       }
+       pts_name = ptsname(pty_fd);
+       if (pts_name == NULL) {
+               mpsslog("can't get pts name\n");
+               goto _close_pty;
+       }
+       printf("%s console message goes to %s\n", mic->name, pts_name);
+       mpsslog("%s console message goes to %s\n", mic->name, pts_name);
+       err = grantpt(pty_fd);
+       if (err < 0) {
+               mpsslog("can't grant access: %s %s\n",
+                       pts_name, strerror(errno));
+               goto _close_pty;
+       }
+       err = unlockpt(pty_fd);
+       if (err < 0) {
+               mpsslog("can't unlock a pseudoterminal: %s %s\n",
+                       pts_name, strerror(errno));
+               goto _close_pty;
+       }
+       console_poll[MONITOR_FD].fd = pty_fd;
+       console_poll[MONITOR_FD].events = POLLIN;
+
+       console_poll[VIRTIO_CONSOLE_FD].fd = mic->mic_console.virtio_console_fd;
+       console_poll[VIRTIO_CONSOLE_FD].events = POLLIN;
+
+       if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd,
+                                 VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr,
+               virtcons_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               goto _close_pty;
+       }
+
+       copy.iovcnt = 1;
+       desc = get_device_desc(mic, VIRTIO_ID_CONSOLE);
+
+       for (;;) {
+               console_poll[MONITOR_FD].revents = 0;
+               console_poll[VIRTIO_CONSOLE_FD].revents = 0;
+               err = poll(console_poll, MAX_CONSOLE_FD, -1);
+               if (err < 0) {
+                       mpsslog("%s %d: poll failed: %s\n", __func__, __LINE__,
+                               strerror(errno));
+                       continue;
+               }
+               if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK))
+                       wait_for_card_driver(mic,
+                                            mic->mic_console.virtio_console_fd,
+                               VIRTIO_ID_CONSOLE);
+
+               if (console_poll[MONITOR_FD].revents & POLLIN) {
+                       copy.iov = iov0;
+                       len = readv(pty_fd, copy.iov, copy.iovcnt);
+                       if (len > 0) {
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read from tap 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       len);
+#endif
+                               spin_for_descriptors(mic, &tx_vr);
+                               txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr,
+                                            &copy, len);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_console.virtio_console_fd,
+                                       &tx_vr, &copy);
+                               if (err < 0) {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                               }
+                               if (!err)
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d wrote to net 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       sum_iovec_len(copy));
+#endif
+                               /* Reinitialize IOV for next run */
+                               iov0->iov_len = PAGE_SIZE;
+                       } else if (len < 0) {
+                               disp_iovec(mic, &copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read failed %s ",
+                                       mic->name, __func__, __LINE__,
+                                       strerror(errno));
+                               mpsslog("cnt %d sum %zd\n",
+                                       copy.iovcnt, sum_iovec_len(&copy));
+                       }
+               }
+
+               if (console_poll[VIRTIO_CONSOLE_FD].revents & POLLIN) {
+                       while (rx_vr.info->avail_idx !=
+                               le16toh(rx_vr.vr.avail->idx)) {
+                               copy.iov = iov1;
+                               txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr,
+                                            &copy, PAGE_SIZE);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_console.virtio_console_fd,
+                                       &rx_vr, &copy);
+                               if (!err) {
+                                       /* Set the correct output iov_len */
+                                       iov1->iov_len = copy.out_len;
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                                       disp_iovec(mic, copy, __func__,
+                                                  __LINE__);
+                                       mpsslog("%s %s %d ",
+                                               mic->name, __func__, __LINE__);
+                                       mpsslog("read from net 0x%lx\n",
+                                               sum_iovec_len(copy));
+#endif
+                                       len = writev(pty_fd,
+                                               copy.iov, copy.iovcnt);
+                                       if (len != sum_iovec_len(&copy)) {
+                                               mpsslog("Tun write failed %s ",
+                                                       strerror(errno));
+                                               mpsslog("len 0x%zx ", len);
+                                               mpsslog("read_len 0x%zx\n",
+                                                       sum_iovec_len(&copy));
+                                       } else {
+#ifdef DEBUG
+                                               disp_iovec(mic, copy, __func__,
+                                                          __LINE__);
+                                               mpsslog("%s %s %d ",
+                                                       mic->name, __func__,
+                                                       __LINE__);
+                                               mpsslog("wrote to tap 0x%lx\n",
+                                                       len);
+#endif
+                                       }
+                               } else {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                                       break;
+                               }
+                       }
+               }
+               if (console_poll[NET_FD_VIRTIO_NET].revents & POLLERR)
+                       mpsslog("%s: %s: POLLERR\n", __func__, mic->name);
+       }
+_close_pty:
+       close(pty_fd);
+_return:
+       pthread_exit(NULL);
+}
+
+static void
+add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd)
+{
+       char path[PATH_MAX];
+       int fd, err;
+
+       snprintf(path, PATH_MAX, "/dev/mic%d", mic->id);
+       fd = open(path, O_RDWR);
+       if (fd < 0) {
+               mpsslog("Could not open %s %s\n", path, strerror(errno));
+               return;
+       }
+
+       err = ioctl(fd, MIC_VIRTIO_ADD_DEVICE, dd);
+       if (err < 0) {
+               mpsslog("Could not add %d %s\n", dd->type, strerror(errno));
+               close(fd);
+               return;
+       }
+       switch (dd->type) {
+       case VIRTIO_ID_NET:
+               mic->mic_net.virtio_net_fd = fd;
+               mpsslog("Added VIRTIO_ID_NET for %s\n", mic->name);
+               break;
+       case VIRTIO_ID_CONSOLE:
+               mic->mic_console.virtio_console_fd = fd;
+               mpsslog("Added VIRTIO_ID_CONSOLE for %s\n", mic->name);
+               break;
+       case VIRTIO_ID_BLOCK:
+               mic->mic_virtblk.virtio_block_fd = fd;
+               mpsslog("Added VIRTIO_ID_BLOCK for %s\n", mic->name);
+               break;
+       }
+}
+
+static bool
+set_backend_file(struct mic_info *mic)
+{
+       FILE *config;
+       char buff[PATH_MAX], *line, *evv, *p;
+
+       snprintf(buff, PATH_MAX, "%s/mpssd%03d.conf", mic_config_dir, mic->id);
+       config = fopen(buff, "r");
+       if (config == NULL)
+               return false;
+       do {  /* look for "virtblk_backend=XXXX" */
+               line = fgets(buff, PATH_MAX, config);
+               if (line == NULL)
+                       break;
+               if (*line == '#')
+                       continue;
+               p = strchr(line, '\n');
+               if (p)
+                       *p = '\0';
+       } while (strncmp(line, virtblk_backend, strlen(virtblk_backend)) != 0);
+       fclose(config);
+       if (line == NULL)
+               return false;
+       evv = strchr(line, '=');
+       if (evv == NULL)
+               return false;
+       mic->mic_virtblk.backend_file = malloc(strlen(evv) + 1);
+       if (mic->mic_virtblk.backend_file == NULL) {
+               mpsslog("%s %d can't allocate memory\n", mic->name, mic->id);
+               return false;
+       }
+       strcpy(mic->mic_virtblk.backend_file, evv + 1);
+       return true;
+}
+
+#define SECTOR_SIZE 512
+static bool
+set_backend_size(struct mic_info *mic)
+{
+       mic->mic_virtblk.backend_size = lseek(mic->mic_virtblk.backend, 0,
+               SEEK_END);
+       if (mic->mic_virtblk.backend_size < 0) {
+               mpsslog("%s: can't seek: %s\n",
+                       mic->name, mic->mic_virtblk.backend_file);
+               return false;
+       }
+       virtblk_dev_page.blk_config.capacity =
+               mic->mic_virtblk.backend_size / SECTOR_SIZE;
+       if ((mic->mic_virtblk.backend_size % SECTOR_SIZE) != 0)
+               virtblk_dev_page.blk_config.capacity++;
+
+       virtblk_dev_page.blk_config.capacity =
+               htole64(virtblk_dev_page.blk_config.capacity);
+
+       return true;
+}
+
+static bool
+open_backend(struct mic_info *mic)
+{
+       if (!set_backend_file(mic))
+               goto _error_exit;
+       mic->mic_virtblk.backend = open(mic->mic_virtblk.backend_file, O_RDWR);
+       if (mic->mic_virtblk.backend < 0) {
+               mpsslog("%s: can't open: %s\n", mic->name,
+                       mic->mic_virtblk.backend_file);
+               goto _error_free;
+       }
+       if (!set_backend_size(mic))
+               goto _error_close;
+       mic->mic_virtblk.backend_addr = mmap(NULL,
+               mic->mic_virtblk.backend_size,
+               PROT_READ|PROT_WRITE, MAP_SHARED,
+               mic->mic_virtblk.backend, 0L);
+       if (mic->mic_virtblk.backend_addr == MAP_FAILED) {
+               mpsslog("%s: can't map: %s %s\n",
+                       mic->name, mic->mic_virtblk.backend_file,
+                       strerror(errno));
+               goto _error_close;
+       }
+       return true;
+
+ _error_close:
+       close(mic->mic_virtblk.backend);
+ _error_free:
+       free(mic->mic_virtblk.backend_file);
+ _error_exit:
+       return false;
+}
+
+static void
+close_backend(struct mic_info *mic)
+{
+       munmap(mic->mic_virtblk.backend_addr, mic->mic_virtblk.backend_size);
+       close(mic->mic_virtblk.backend);
+       free(mic->mic_virtblk.backend_file);
+}
+
+static bool
+start_virtblk(struct mic_info *mic, struct mic_vring *vring)
+{
+       if (((unsigned long)&virtblk_dev_page.blk_config % 8) != 0) {
+               mpsslog("%s: blk_config is not 8 byte aligned.\n",
+                       mic->name);
+               return false;
+       }
+       add_virtio_device(mic, &virtblk_dev_page.dd);
+       if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd,
+                                 VIRTIO_ID_BLOCK, vring, NULL,
+                                 virtblk_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               return false;
+       }
+       return true;
+}
+
+static void
+stop_virtblk(struct mic_info *mic)
+{
+       int vr_size, ret;
+
+       vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES,
+               MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info));
+       ret = munmap(mic->mic_virtblk.block_dp,
+               MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq);
+       if (ret < 0)
+               mpsslog("%s munmap errno %d\n", mic->name, errno);
+       close(mic->mic_virtblk.virtio_block_fd);
+}
+
+static __u8
+header_error_check(struct vring_desc *desc)
+{
+       if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) {
+               mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) {
+               mpsslog("%s() %d: alone\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       if (le16toh(desc->flags) & VRING_DESC_F_WRITE) {
+               mpsslog("%s() %d: not read\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int
+read_header(int fd, struct virtio_blk_outhdr *hdr, __u32 desc_idx)
+{
+       struct iovec iovec;
+       struct mic_copy_desc copy;
+
+       iovec.iov_len = sizeof(*hdr);
+       iovec.iov_base = hdr;
+       copy.iov = &iovec;
+       copy.iovcnt = 1;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = false;  /* do not update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+static int
+transfer_blocks(int fd, struct iovec *iovec, __u32 iovcnt)
+{
+       struct mic_copy_desc copy;
+
+       copy.iov = iovec;
+       copy.iovcnt = iovcnt;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = false;  /* do not update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+static __u8
+status_error_check(struct vring_desc *desc)
+{
+       if (le32toh(desc->len) != sizeof(__u8)) {
+               mpsslog("%s() %d: length is not sizeof(status)\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int
+write_status(int fd, __u8 *status)
+{
+       struct iovec iovec;
+       struct mic_copy_desc copy;
+
+       iovec.iov_base = status;
+       iovec.iov_len = sizeof(*status);
+       copy.iov = &iovec;
+       copy.iovcnt = 1;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = true; /* Update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+static void *
+virtio_block(void *arg)
+{
+       struct mic_info *mic = (struct mic_info *)arg;
+       int ret;
+       struct pollfd block_poll;
+       struct mic_vring vring;
+       __u16 avail_idx;
+       __u32 desc_idx;
+       struct vring_desc *desc;
+       struct iovec *iovec, *piov;
+       __u8 status;
+       __u32 buffer_desc_idx;
+       struct virtio_blk_outhdr hdr;
+       void *fos;
+
+       for (;;) {  /* forever */
+               if (!open_backend(mic)) { /* No virtblk */
+                       for (mic->mic_virtblk.signaled = 0;
+                               !mic->mic_virtblk.signaled;)
+                               sleep(1);
+                       continue;
+               }
+
+               /* backend file is specified. */
+               if (!start_virtblk(mic, &vring))
+                       goto _close_backend;
+               iovec = malloc(sizeof(*iovec) *
+                       le32toh(virtblk_dev_page.blk_config.seg_max));
+               if (!iovec) {
+                       mpsslog("%s: can't alloc iovec: %s\n",
+                               mic->name, strerror(ENOMEM));
+                       goto _stop_virtblk;
+               }
+
+               block_poll.fd = mic->mic_virtblk.virtio_block_fd;
+               block_poll.events = POLLIN;
+               for (mic->mic_virtblk.signaled = 0;
+                    !mic->mic_virtblk.signaled;) {
+                       block_poll.revents = 0;
+                                       /* timeout in 1 sec to see signaled */
+                       ret = poll(&block_poll, 1, 1000);
+                       if (ret < 0) {
+                               mpsslog("%s %d: poll failed: %s\n",
+                                       __func__, __LINE__,
+                                       strerror(errno));
+                               continue;
+                       }
+
+                       if (!(block_poll.revents & POLLIN)) {
+#ifdef DEBUG
+                               mpsslog("%s %d: block_poll.revents=0x%x\n",
+                                       __func__, __LINE__, block_poll.revents);
+#endif
+                               continue;
+                       }
+
+                       /* POLLIN */
+                       while (vring.info->avail_idx !=
+                               le16toh(vring.vr.avail->idx)) {
+                               /* read header element */
+                               avail_idx =
+                                       vring.info->avail_idx &
+                                       (vring.vr.num - 1);
+                               desc_idx = le16toh(
+                                       vring.vr.avail->ring[avail_idx]);
+                               desc = &vring.vr.desc[desc_idx];
+#ifdef DEBUG
+                               mpsslog("%s() %d: avail_idx=%d ",
+                                       __func__, __LINE__,
+                                       vring.info->avail_idx);
+                               mpsslog("vring.vr.num=%d desc=%p\n",
+                                       vring.vr.num, desc);
+#endif
+                               status = header_error_check(desc);
+                               ret = read_header(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                       &hdr, desc_idx);
+                               if (ret < 0) {
+                                       mpsslog("%s() %d %s: ret=%d %s\n",
+                                               __func__, __LINE__,
+                                               mic->name, ret,
+                                               strerror(errno));
+                                       break;
+                               }
+                               /* buffer element */
+                               piov = iovec;
+                               status = 0;
+                               fos = mic->mic_virtblk.backend_addr +
+                                       (hdr.sector * SECTOR_SIZE);
+                               buffer_desc_idx = next_desc(desc);
+                               desc_idx = buffer_desc_idx;
+                               for (desc = &vring.vr.desc[buffer_desc_idx];
+                                    desc->flags & VRING_DESC_F_NEXT;
+                                    desc_idx = next_desc(desc),
+                                            desc = &vring.vr.desc[desc_idx]) {
+                                       piov->iov_len = desc->len;
+                                       piov->iov_base = fos;
+                                       piov++;
+                                       fos += desc->len;
+                               }
+                               /* Returning NULLs for VIRTIO_BLK_T_GET_ID. */
+                               if (hdr.type & ~(VIRTIO_BLK_T_OUT |
+                                       VIRTIO_BLK_T_GET_ID)) {
+                                       /*
+                                         VIRTIO_BLK_T_IN - does not do
+                                         anything. Probably for documenting.
+                                         VIRTIO_BLK_T_SCSI_CMD - for
+                                         virtio_scsi.
+                                         VIRTIO_BLK_T_FLUSH - turned off in
+                                         config space.
+                                         VIRTIO_BLK_T_BARRIER - defined but not
+                                         used in anywhere.
+                                       */
+                                       mpsslog("%s() %d: type %x ",
+                                               __func__, __LINE__,
+                                               hdr.type);
+                                       mpsslog("is not supported\n");
+                                       status = -ENOTSUP;
+
+                               } else {
+                                       ret = transfer_blocks(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                               iovec,
+                                               piov - iovec);
+                                       if (ret < 0 &&
+                                           status != 0)
+                                               status = ret;
+                               }
+                               /* write status and update used pointer */
+                               if (status != 0)
+                                       status = status_error_check(desc);
+                               ret = write_status(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                       &status);
+#ifdef DEBUG
+                               mpsslog("%s() %d: write status=%d on desc=%p\n",
+                                       __func__, __LINE__,
+                                       status, desc);
+#endif
+                       }
+               }
+               free(iovec);
+_stop_virtblk:
+               stop_virtblk(mic);
+_close_backend:
+               close_backend(mic);
+       }  /* forever */
+
+       pthread_exit(NULL);
+}
+
+static void
+reset(struct mic_info *mic)
+{
+#define RESET_TIMEOUT 120
+       int i = RESET_TIMEOUT;
+       setsysfs(mic->name, "state", "reset");
+       while (i) {
+               char *state;
+               state = readsysfs(mic->name, "state");
+               if (!state)
+                       goto retry;
+               mpsslog("%s: %s %d state %s\n",
+                       mic->name, __func__, __LINE__, state);
+
+               /*
+                * If the shutdown was initiated by OSPM, the state stays
+                * in "suspended" which is also a valid condition for reset.
+                */
+               if ((!strcmp(state, "offline")) ||
+                   (!strcmp(state, "suspended"))) {
+                       free(state);
+                       break;
+               }
+               free(state);
+retry:
+               sleep(1);
+               i--;
+       }
+}
+
+static int
+get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status)
+{
+       if (!strcmp(shutdown_status, "nop"))
+               return MIC_NOP;
+       if (!strcmp(shutdown_status, "crashed"))
+               return MIC_CRASHED;
+       if (!strcmp(shutdown_status, "halted"))
+               return MIC_HALTED;
+       if (!strcmp(shutdown_status, "poweroff"))
+               return MIC_POWER_OFF;
+       if (!strcmp(shutdown_status, "restart"))
+               return MIC_RESTART;
+       mpsslog("%s: BUG invalid status %s\n", mic->name, shutdown_status);
+       /* Invalid state */
+       assert(0);
+};
+
+static int get_mic_state(struct mic_info *mic, char *state)
+{
+       if (!strcmp(state, "offline"))
+               return MIC_OFFLINE;
+       if (!strcmp(state, "online"))
+               return MIC_ONLINE;
+       if (!strcmp(state, "shutting_down"))
+               return MIC_SHUTTING_DOWN;
+       if (!strcmp(state, "reset_failed"))
+               return MIC_RESET_FAILED;
+       if (!strcmp(state, "suspending"))
+               return MIC_SUSPENDING;
+       if (!strcmp(state, "suspended"))
+               return MIC_SUSPENDED;
+       mpsslog("%s: BUG invalid state %s\n", mic->name, state);
+       /* Invalid state */
+       assert(0);
+};
+
+static void mic_handle_shutdown(struct mic_info *mic)
+{
+#define SHUTDOWN_TIMEOUT 60
+       int i = SHUTDOWN_TIMEOUT, ret, stat = 0;
+       char *shutdown_status;
+       while (i) {
+               shutdown_status = readsysfs(mic->name, "shutdown_status");
+               if (!shutdown_status)
+                       continue;
+               mpsslog("%s: %s %d shutdown_status %s\n",
+                       mic->name, __func__, __LINE__, shutdown_status);
+               switch (get_mic_shutdown_status(mic, shutdown_status)) {
+               case MIC_RESTART:
+                       mic->restart = 1;
+               case MIC_HALTED:
+               case MIC_POWER_OFF:
+               case MIC_CRASHED:
+                       free(shutdown_status);
+                       goto reset;
+               default:
+                       break;
+               }
+               free(shutdown_status);
+               sleep(1);
+               i--;
+       }
+reset:
+       ret = kill(mic->pid, SIGTERM);
+       mpsslog("%s: %s %d kill pid %d ret %d\n",
+               mic->name, __func__, __LINE__,
+               mic->pid, ret);
+       if (!ret) {
+               ret = waitpid(mic->pid, &stat,
+                       WIFSIGNALED(stat));
+               mpsslog("%s: %s %d waitpid ret %d pid %d\n",
+                       mic->name, __func__, __LINE__,
+                       ret, mic->pid);
+       }
+       if (ret == mic->pid)
+               reset(mic);
+}
+
+static void *
+mic_config(void *arg)
+{
+       struct mic_info *mic = (struct mic_info *)arg;
+       char *state = NULL;
+       char pathname[PATH_MAX];
+       int fd, ret;
+       struct pollfd ufds[1];
+       char value[4096];
+
+       snprintf(pathname, PATH_MAX - 1, "%s/%s/%s",
+                MICSYSFSDIR, mic->name, "state");
+
+       fd = open(pathname, O_RDONLY);
+       if (fd < 0) {
+               mpsslog("%s: opening file %s failed %s\n",
+                       mic->name, pathname, strerror(errno));
+               goto error;
+       }
+
+       do {
+               ret = read(fd, value, sizeof(value));
+               if (ret < 0) {
+                       mpsslog("%s: Failed to read sysfs entry '%s': %s\n",
+                               mic->name, pathname, strerror(errno));
+                       goto close_error1;
+               }
+retry:
+               state = readsysfs(mic->name, "state");
+               if (!state)
+                       goto retry;
+               mpsslog("%s: %s %d state %s\n",
+                       mic->name, __func__, __LINE__, state);
+               switch (get_mic_state(mic, state)) {
+               case MIC_SHUTTING_DOWN:
+                       mic_handle_shutdown(mic);
+                       goto close_error;
+               case MIC_SUSPENDING:
+                       mic->boot_on_resume = 1;
+                       setsysfs(mic->name, "state", "suspend");
+                       mic_handle_shutdown(mic);
+                       goto close_error;
+               case MIC_OFFLINE:
+                       if (mic->boot_on_resume) {
+                               setsysfs(mic->name, "state", "boot");
+                               mic->boot_on_resume = 0;
+                       }
+                       break;
+               default:
+                       break;
+               }
+               free(state);
+
+               ufds[0].fd = fd;
+               ufds[0].events = POLLERR | POLLPRI;
+               ret = poll(ufds, 1, -1);
+               if (ret < 0) {
+                       mpsslog("%s: poll failed %s\n",
+                               mic->name, strerror(errno));
+                       goto close_error1;
+               }
+       } while (1);
+close_error:
+       free(state);
+close_error1:
+       close(fd);
+error:
+       init_mic(mic);
+       pthread_exit(NULL);
+}
+
+static void
+set_cmdline(struct mic_info *mic)
+{
+       char buffer[PATH_MAX];
+       int len;
+
+       len = snprintf(buffer, PATH_MAX,
+               "clocksource=tsc highres=off nohz=off ");
+       len += snprintf(buffer + len, PATH_MAX,
+               "cpufreq_on;corec6_off;pc3_off;pc6_off ");
+       len += snprintf(buffer + len, PATH_MAX,
+               "ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0",
+               mic->id);
+
+       setsysfs(mic->name, "cmdline", buffer);
+       mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer);
+       snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id);
+       mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer);
+}
+
+static void
+set_log_buf_info(struct mic_info *mic)
+{
+       int fd;
+       off_t len;
+       char system_map[] = "/lib/firmware/mic/System.map";
+       char *map, *temp, log_buf[17] = {'\0'};
+
+       fd = open(system_map, O_RDONLY);
+       if (fd < 0) {
+               mpsslog("%s: Opening System.map failed: %d\n",
+                       mic->name, errno);
+               return;
+       }
+       len = lseek(fd, 0, SEEK_END);
+       if (len < 0) {
+               mpsslog("%s: Reading System.map size failed: %d\n",
+                       mic->name, errno);
+               close(fd);
+               return;
+       }
+       map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (map == MAP_FAILED) {
+               mpsslog("%s: mmap of System.map failed: %d\n",
+                       mic->name, errno);
+               close(fd);
+               return;
+       }
+       temp = strstr(map, "__log_buf");
+       if (!temp) {
+               mpsslog("%s: __log_buf not found: %d\n", mic->name, errno);
+               munmap(map, len);
+               close(fd);
+               return;
+       }
+       strncpy(log_buf, temp - 19, 16);
+       setsysfs(mic->name, "log_buf_addr", log_buf);
+       mpsslog("%s: log_buf_addr: %s\n", mic->name, log_buf);
+       temp = strstr(map, "log_buf_len");
+       if (!temp) {
+               mpsslog("%s: log_buf_len not found: %d\n", mic->name, errno);
+               munmap(map, len);
+               close(fd);
+               return;
+       }
+       strncpy(log_buf, temp - 19, 16);
+       setsysfs(mic->name, "log_buf_len", log_buf);
+       mpsslog("%s: log_buf_len: %s\n", mic->name, log_buf);
+       munmap(map, len);
+       close(fd);
+}
+
+static void init_mic(struct mic_info *mic);
+
+static void
+change_virtblk_backend(int x, siginfo_t *siginfo, void *p)
+{
+       struct mic_info *mic;
+
+       for (mic = mic_list.next; mic != NULL; mic = mic->next)
+               mic->mic_virtblk.signaled = 1/* true */;
+}
+
+static void
+init_mic(struct mic_info *mic)
+{
+       struct sigaction ignore = {
+               .sa_flags = 0,
+               .sa_handler = SIG_IGN
+       };
+       struct sigaction act = {
+               .sa_flags = SA_SIGINFO,
+               .sa_sigaction = change_virtblk_backend,
+       };
+       char buffer[PATH_MAX];
+       int err;
+
+       /*
+        * Currently, one virtio block device is supported for each MIC card
+        * at a time. Any user (or test) can send a SIGUSR1 to the MIC daemon.
+        * The signal informs the virtio block backend about a change in the
+        * configuration file which specifies the virtio backend file name on
+        * the host. Virtio block backend then re-reads the configuration file
+        * and switches to the new block device. This signalling mechanism may
+        * not be required once multiple virtio block devices are supported by
+        * the MIC daemon.
+        */
+       sigaction(SIGUSR1, &ignore, NULL);
+
+       mic->pid = fork();
+       switch (mic->pid) {
+       case 0:
+               set_log_buf_info(mic);
+               set_cmdline(mic);
+               add_virtio_device(mic, &virtcons_dev_page.dd);
+               add_virtio_device(mic, &virtnet_dev_page.dd);
+               err = pthread_create(&mic->mic_console.console_thread, NULL,
+                       virtio_console, mic);
+               if (err)
+                       mpsslog("%s virtcons pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               err = pthread_create(&mic->mic_net.net_thread, NULL,
+                       virtio_net, mic);
+               if (err)
+                       mpsslog("%s virtnet pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               err = pthread_create(&mic->mic_virtblk.block_thread, NULL,
+                       virtio_block, mic);
+               if (err)
+                       mpsslog("%s virtblk pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               sigemptyset(&act.sa_mask);
+               err = sigaction(SIGUSR1, &act, NULL);
+               if (err)
+                       mpsslog("%s sigaction SIGUSR1 failed %s\n",
+                               mic->name, strerror(errno));
+               while (1)
+                       sleep(60);
+       case -1:
+               mpsslog("fork failed MIC name %s id %d errno %d\n",
+                       mic->name, mic->id, errno);
+               break;
+       default:
+               if (mic->restart) {
+                       snprintf(buffer, PATH_MAX, "boot");
+                       setsysfs(mic->name, "state", buffer);
+                       mpsslog("%s restarting mic %d\n",
+                               mic->name, mic->restart);
+                       mic->restart = 0;
+               }
+               pthread_create(&mic->config_thread, NULL, mic_config, mic);
+       }
+}
+
+static void
+start_daemon(void)
+{
+       struct mic_info *mic;
+
+       for (mic = mic_list.next; mic != NULL; mic = mic->next)
+               init_mic(mic);
+
+       while (1)
+               sleep(60);
+}
+
+static int
+init_mic_list(void)
+{
+       struct mic_info *mic = &mic_list;
+       struct dirent *file;
+       DIR *dp;
+       int cnt = 0;
+
+       dp = opendir(MICSYSFSDIR);
+       if (!dp)
+               return 0;
+
+       while ((file = readdir(dp)) != NULL) {
+               if (!strncmp(file->d_name, "mic", 3)) {
+                       mic->next = calloc(1, sizeof(struct mic_info));
+                       if (mic->next) {
+                               mic = mic->next;
+                               mic->id = atoi(&file->d_name[3]);
+                               mic->name = malloc(strlen(file->d_name) + 16);
+                               if (mic->name)
+                                       strcpy(mic->name, file->d_name);
+                               mpsslog("MIC name %s id %d\n", mic->name,
+                                       mic->id);
+                               cnt++;
+                       }
+               }
+       }
+
+       closedir(dp);
+       return cnt;
+}
+
+void
+mpsslog(char *format, ...)
+{
+       va_list args;
+       char buffer[4096];
+       char ts[52], *ts1;
+       time_t t;
+
+       if (logfp == NULL)
+               return;
+
+       va_start(args, format);
+       vsprintf(buffer, format, args);
+       va_end(args);
+
+       time(&t);
+       ts1 = ctime_r(&t, ts);
+       ts1[strlen(ts1) - 1] = '\0';
+       fprintf(logfp, "%s: %s", ts1, buffer);
+
+       fflush(logfp);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int cnt;
+       pid_t pid;
+
+       myname = argv[0];
+
+       logfp = fopen(LOGFILE_NAME, "a+");
+       if (!logfp) {
+               fprintf(stderr, "cannot open logfile '%s'\n", LOGFILE_NAME);
+               exit(1);
+       }
+       pid = fork();
+       switch (pid) {
+       case 0:
+               break;
+       case -1:
+               exit(2);
+       default:
+               exit(0);
+       }
+
+       mpsslog("MIC Daemon start\n");
+
+       cnt = init_mic_list();
+       if (cnt == 0) {
+               mpsslog("MIC module not loaded\n");
+               exit(3);
+       }
+       mpsslog("MIC found %d devices\n", cnt);
+
+       start_daemon();
+
+       exit(0);
+}
diff --git a/Documentation/mic/mpssd/mpssd.h b/Documentation/mic/mpssd/mpssd.h
new file mode 100644 (file)
index 0000000..f5f18b1
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+#ifndef _MPSSD_H_
+#define _MPSSD_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/dir.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <signal.h>
+#include <limits.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_tun.h>
+#include <linux/virtio_ids.h>
+
+#define MICSYSFSDIR "/sys/class/mic"
+#define LOGFILE_NAME "/var/log/mpssd"
+#define PAGE_SIZE 4096
+
+struct mic_console_info {
+       pthread_t       console_thread;
+       int             virtio_console_fd;
+       void            *console_dp;
+};
+
+struct mic_net_info {
+       pthread_t       net_thread;
+       int             virtio_net_fd;
+       int             tap_fd;
+       void            *net_dp;
+};
+
+struct mic_virtblk_info {
+       pthread_t       block_thread;
+       int             virtio_block_fd;
+       void            *block_dp;
+       volatile sig_atomic_t   signaled;
+       char            *backend_file;
+       int             backend;
+       void            *backend_addr;
+       long            backend_size;
+};
+
+struct mic_info {
+       int             id;
+       char            *name;
+       pthread_t       config_thread;
+       pid_t           pid;
+       struct mic_console_info mic_console;
+       struct mic_net_info     mic_net;
+       struct mic_virtblk_info mic_virtblk;
+       int             restart;
+       int             boot_on_resume;
+       struct mic_info *next;
+};
+
+__attribute__((format(printf, 1, 2)))
+void mpsslog(char *format, ...);
+char *readsysfs(char *dir, char *entry);
+int setsysfs(char *dir, char *entry, char *value);
+#endif
diff --git a/Documentation/mic/mpssd/sysfs.c b/Documentation/mic/mpssd/sysfs.c
new file mode 100644 (file)
index 0000000..8dd3269
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+
+#include "mpssd.h"
+
+#define PAGE_SIZE 4096
+
+char *
+readsysfs(char *dir, char *entry)
+{
+       char filename[PATH_MAX];
+       char value[PAGE_SIZE];
+       char *string = NULL;
+       int fd;
+       int len;
+
+       if (dir == NULL)
+               snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry);
+       else
+               snprintf(filename, PATH_MAX,
+                        "%s/%s/%s", MICSYSFSDIR, dir, entry);
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               mpsslog("Failed to open sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               return NULL;
+       }
+
+       len = read(fd, value, sizeof(value));
+       if (len < 0) {
+               mpsslog("Failed to read sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               goto readsys_ret;
+       }
+       if (len == 0)
+               goto readsys_ret;
+
+       value[len - 1] = '\0';
+
+       string = malloc(strlen(value) + 1);
+       if (string)
+               strcpy(string, value);
+
+readsys_ret:
+       close(fd);
+       return string;
+}
+
+int
+setsysfs(char *dir, char *entry, char *value)
+{
+       char filename[PATH_MAX];
+       char *oldvalue;
+       int fd, ret = 0;
+
+       if (dir == NULL)
+               snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry);
+       else
+               snprintf(filename, PATH_MAX, "%s/%s/%s",
+                        MICSYSFSDIR, dir, entry);
+
+       oldvalue = readsysfs(dir, entry);
+
+       fd = open(filename, O_RDWR);
+       if (fd < 0) {
+               ret = errno;
+               mpsslog("Failed to open sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               goto done;
+       }
+
+       if (!oldvalue || strcmp(value, oldvalue)) {
+               if (write(fd, value, strlen(value)) < 0) {
+                       ret = errno;
+                       mpsslog("Failed to write new sysfs entry '%s': %s\n",
+                               filename, strerror(errno));
+               }
+       }
+       close(fd);
+done:
+       if (oldvalue)
+               free(oldvalue);
+       return ret;
+}
index d718bc2ff1cfc4e1e8d54d3002657dd8400086e3..bf5dbe3ab8c51ed25fea951249af0393105d2329 100644 (file)
@@ -18,8 +18,8 @@ Introduction
 Datagram Congestion Control Protocol (DCCP) is an unreliable, connection
 oriented protocol designed to solve issues present in UDP and TCP, particularly
 for real-time and multimedia (streaming) traffic.
-It divides into a base protocol (RFC 4340) and plugable congestion control
-modules called CCIDs. Like plugable TCP congestion control, at least one CCID
+It divides into a base protocol (RFC 4340) and pluggable congestion control
+modules called CCIDs. Like pluggable TCP congestion control, at least one CCID
 needs to be enabled in order for the protocol to function properly. In the Linux
 implementation, this is the TCP-like CCID2 (RFC 4341). Additional CCIDs, such as
 the TCP-friendly CCID3 (RFC 4342), are optional.
index 13a32124bca074b9722818809969899ff602c407..f862cf3aff3495d96f699cd666af6dbf3d18a453 100644 (file)
@@ -103,7 +103,7 @@ Additional Configurations
   PRO/100 Family of Adapters is e100.
 
   As an example, if you install the e100 driver for two PRO/100 adapters
-  (eth0 and eth1), add the following to a configuraton file in /etc/modprobe.d/
+  (eth0 and eth1), add the following to a configuration file in /etc/modprobe.d/
 
        alias eth0 e100
        alias eth1 e100
index 09eb57329f115d17d65c37eac449d0270859b0f7..22bbc7225f8ed599e8c8b51653c017a6c4845c53 100644 (file)
@@ -4,7 +4,7 @@
 
 Introduction
 ============
-The IEEE 802.15.4 working group focuses on standartization of bottom
+The IEEE 802.15.4 working group focuses on standardization of bottom
 two layers: Medium Access Control (MAC) and Physical (PHY). And there
 are mainly two options available for upper layers:
  - ZigBee - proprietary protocol from ZigBee Alliance
@@ -66,7 +66,7 @@ net_device, with .type = ARPHRD_IEEE802154. Data is exchanged with socket family
 code via plain sk_buffs. On skb reception skb->cb must contain additional
 info as described in the struct ieee802154_mac_cb. During packet transmission
 the skb->cb is used to provide additional data to device's header_ops->create
-function. Be aware, that this data can be overriden later (when socket code
+function. Be aware that this data can be overridden later (when socket code
 submits skb to qdisc), so if you need something from that cb later, you should
 store info in the skb->data on your own.
 
index e63fc1f7bf875ad6908b4a7ff3ade9a72f14557d..c74434de2fa50a5873c9cef9900bc47fd6431839 100644 (file)
@@ -197,7 +197,7 @@ state information because the file format is subject to change. It is
 implemented to provide extra debug information to help diagnose
 problems.) Users should use the netlink API.
 
-/proc/net/pppol2tp is also provided for backwards compaibility with
+/proc/net/pppol2tp is also provided for backwards compatibility with
 the original pppol2tp driver. It lists information about L2TPv2
 tunnels and sessions only. Its use is discouraged.
 
index d9112f01c44a384f4c076b6a0eb27f9adf7fda46..0fe1c6e0dbcd58fccdcc953477da11e2d6598358 100644 (file)
@@ -4,23 +4,23 @@ Information you need to know about netdev
 
 Q: What is netdev?
 
-A: It is a mailing list for all network related linux stuff.  This includes
+A: It is a mailing list for all network-related Linux stuff.  This includes
    anything found under net/  (i.e. core code like IPv6) and drivers/net
-   (i.e. hardware specific drivers) in the linux source tree.
+   (i.e. hardware specific drivers) in the Linux source tree.
 
    Note that some subsystems (e.g. wireless drivers) which have a high volume
    of traffic have their own specific mailing lists.
 
-   The netdev list is managed (like many other linux mailing lists) through
+   The netdev list is managed (like many other Linux mailing lists) through
    VGER ( http://vger.kernel.org/ ) and archives can be found below:
 
        http://marc.info/?l=linux-netdev
        http://www.spinics.net/lists/netdev/
 
-   Aside from subsystems like that mentioned above, all network related linux
-   development (i.e. RFC, review, comments, etc) takes place on netdev.
+   Aside from subsystems like that mentioned above, all network-related Linux
+   development (i.e. RFC, review, comments, etc.) takes place on netdev.
 
-Q: How do the changes posted to netdev make their way into linux?
+Q: How do the changes posted to netdev make their way into Linux?
 
 A: There are always two trees (git repositories) in play.  Both are driven
    by David Miller, the main network maintainer.  There is the "net" tree,
@@ -35,7 +35,7 @@ A: There are always two trees (git repositories) in play.  Both are driven
 Q: How often do changes from these trees make it to the mainline Linus tree?
 
 A: To understand this, you need to know a bit of background information
-   on the cadence of linux development.  Each new release starts off with
+   on the cadence of Linux development.  Each new release starts off with
    a two week "merge window" where the main maintainers feed their new
    stuff to Linus for merging into the mainline tree.  After the two weeks,
    the merge window is closed, and it is called/tagged "-rc1".  No new
@@ -46,7 +46,7 @@ A: To understand this, you need to know a bit of background information
    things are in a state of churn), and a week after the last vX.Y-rcN
    was done, the official "vX.Y" is released.
 
-   Relating that to netdev:  At the beginning of the 2 week merge window,
+   Relating that to netdev:  At the beginning of the 2-week merge window,
    the net-next tree will be closed - no new changes/features.  The
    accumulated new content of the past ~10 weeks will be passed onto
    mainline/Linus via a pull request for vX.Y -- at the same time,
@@ -59,16 +59,16 @@ A: To understand this, you need to know a bit of background information
    IMPORTANT:  Do not send new net-next content to netdev during the
    period during which net-next tree is closed.
 
-   Shortly after the two weeks have passed, (and vX.Y-rc1 is released) the
+   Shortly after the two weeks have passed (and vX.Y-rc1 is released), the
    tree for net-next reopens to collect content for the next (vX.Y+1) release.
 
    If you aren't subscribed to netdev and/or are simply unsure if net-next
    has re-opened yet, simply check the net-next git repository link above for
-   any new networking related commits.
+   any new networking-related commits.
 
    The "net" tree continues to collect fixes for the vX.Y content, and
    is fed back to Linus at regular (~weekly) intervals.  Meaning that the
-   focus for "net" is on stablilization and bugfixes.
+   focus for "net" is on stabilization and bugfixes.
 
    Finally, the vX.Y gets released, and the whole cycle starts over.
 
@@ -217,7 +217,7 @@ A: Attention to detail.  Re-read your own work as if you were the
    to why it happens, and then if necessary, explain why the fix proposed
    is the best way to get things done.   Don't mangle whitespace, and as
    is common, don't mis-indent function arguments that span multiple lines.
-   If it is your 1st patch, mail it to yourself so you can test apply
+   If it is your first patch, mail it to yourself so you can test apply
    it to an unpatched tree to confirm infrastructure didn't mangle it.
 
    Finally, go back and read Documentation/SubmittingPatches to be
index 533378839546f9271022189c96ee9d440b44e9ae..b26122973525f81e691f2ef05e7069647d6b7e19 100644 (file)
@@ -45,7 +45,7 @@ processing.
 
 Conversion of the reception path involves calling poll() on the file
 descriptor, once the socket is readable the frames from the ring are
-processsed in order until no more messages are available, as indicated by
+processed in order until no more messages are available, as indicated by
 a status word in the frame header.
 
 On kernel side, in order to make use of memory mapped I/O on receive, the
@@ -56,7 +56,7 @@ Dumps of kernel databases automatically support memory mapped I/O.
 
 Conversion of the transmit path involves changing message construction to
 use memory from the TX ring instead of (usually) a buffer declared on the
-stack and setting up the frame header approriately. Optionally poll() can
+stack and setting up the frame header appropriately. Optionally poll() can
 be used to wait for free frames in the TX ring.
 
 Structured and definitions for using memory mapped I/O are contained in
@@ -231,7 +231,7 @@ Ring setup:
        if (setsockopt(fd, NETLINK_TX_RING, &req, sizeof(req)) < 0)
                exit(1)
 
-       /* Calculate size of each invididual ring */
+       /* Calculate size of each individual ring */
        ring_size = req.nm_block_nr * req.nm_block_size;
 
        /* Map RX/TX rings. The TX ring is located after the RX ring */
index 97694572338be53d1cfddd03b4112e7ef6a771a8..355c6d8ef8ad7bff92071f45ebfbf085f9115df9 100644 (file)
@@ -89,8 +89,8 @@ packets. The name 'carrier' and the inversion are historical, think of
 it as lower layer.
 
 Note that for certain kind of soft-devices, which are not managing any
-real hardware, there is possible to set this bit from userpsace.
-One should use TVL IFLA_CARRIER to do so.
+real hardware, it is possible to set this bit from userspace.  One
+should use TVL IFLA_CARRIER to do so.
 
 netif_carrier_ok() can be used to query that bit.
 
index 60d05eb77c6429f883d342e95de4546c50e759a2..b89bc82eed4656430ab2d4d4a6454d08ac44624f 100644 (file)
@@ -144,7 +144,7 @@ An overview of the RxRPC protocol:
  (*) Calls use ACK packets to handle reliability.  Data packets are also
      explicitly sequenced per call.
 
- (*) There are two types of positive acknowledgement: hard-ACKs and soft-ACKs.
+ (*) There are two types of positive acknowledgment: hard-ACKs and soft-ACKs.
      A hard-ACK indicates to the far side that all the data received to a point
      has been received and processed; a soft-ACK indicates that the data has
      been received but may yet be discarded and re-requested.  The sender may
index 457b8bbafb08061a3bbe523e0875e84c8289496a..cdd916da838d44317974161311d1f81e2db2c233 100644 (file)
@@ -160,7 +160,7 @@ Where:
  o pmt: core has the embedded power module (optional).
  o force_sf_dma_mode: force DMA to use the Store and Forward mode
                     instead of the Threshold.
- o force_thresh_dma_mode: force DMA to use the Shreshold mode other than
+ o force_thresh_dma_mode: force DMA to use the Threshold mode other than
                     the Store and Forward mode.
  o riwt_off: force to disable the RX watchdog feature and switch to NAPI mode.
  o fix_mac_speed: this callback is used for modifying some syscfg registers
@@ -175,7 +175,7 @@ Where:
             registers.
  o custom_cfg/custom_data: this is a custom configuration that can be passed
                           while initializing the resources.
- o bsp_priv: another private poiter.
+ o bsp_priv: another private pointer.
 
 For MDIO bus The we have:
 
@@ -271,7 +271,7 @@ reset procedure etc).
  o dwmac1000_dma.c:  dma functions for the GMAC chip;
  o dwmac1000.h: specific header file for the GMAC;
  o dwmac100_core: MAC 100 core and dma code;
- o dwmac100_dma.c: dma funtions for the MAC chip;
+ o dwmac100_dma.c: dma functions for the MAC chip;
  o dwmac1000.h: specific header file for the MAC;
  o dwmac_lib.c: generic DMA functions shared among chips;
  o enh_desc.c: functions for handling enhanced descriptors;
@@ -364,4 +364,4 @@ Auto-negotiated Link Parter Ability.
 10) TODO:
  o XGMAC is not supported.
  o Complete the TBI & RTBI support.
- o extened VLAN support for 3.70a SYNP GMAC.
+ o extend VLAN support for 3.70a SYNP GMAC.
index 9a8041dcbb53209a1861edc4032729364a81998d..97282da82b75c150158c84172910f9765d8b7eef 100644 (file)
@@ -68,7 +68,7 @@ Module parameters
 
 There are several parameters which may be provided to the driver when
 its module is loaded.  These are usually placed in /etc/modprobe.d/*.conf
-configuretion files.  Example:
+configuration files.  Example:
 
 options 3c59x debug=3 rx_copybreak=300
 
@@ -178,7 +178,7 @@ max_interrupt_work=N
 
   The driver's interrupt service routine can handle many receive and
   transmit packets in a single invocation.  It does this in a loop. 
-  The value of max_interrupt_work governs how mnay times the interrupt
+  The value of max_interrupt_work governs how many times the interrupt
   service routine will loop.  The default value is 32 loops.  If this
   is exceeded the interrupt service routine gives up and generates a
   warning message "eth0: Too much work in interrupt".
index 78f662ee0622abb312aff56f13b97768c0bff81f..7f213b556e85aaa493aeef6e0b9e36c654c30342 100644 (file)
@@ -105,7 +105,7 @@ reduced by the following measures or a combination thereof:
     later.
     The lapb module interface was modified to support this. Its
     data_indication() method should now transparently pass the
-    netif_rx() return value to the (lapb mopdule) caller.
+    netif_rx() return value to the (lapb module) caller.
 (2) Drivers for kernel versions 2.2.x should always check the global
     variable netdev_dropping when a new frame is received. The driver
     should only call netif_rx() if netdev_dropping is zero. Otherwise
diff --git a/Documentation/phy.txt b/Documentation/phy.txt
new file mode 100644 (file)
index 0000000..0103e4b
--- /dev/null
@@ -0,0 +1,166 @@
+                           PHY SUBSYSTEM
+                 Kishon Vijay Abraham I <kishon@ti.com>
+
+This document explains the Generic PHY Framework along with the APIs provided,
+and how-to-use.
+
+1. Introduction
+
+*PHY* is the abbreviation for physical layer. It is used to connect a device
+to the physical medium e.g., the USB controller has a PHY to provide functions
+such as serialization, de-serialization, encoding, decoding and is responsible
+for obtaining the required data transmission rate. Note that some USB
+controllers have PHY functionality embedded into it and others use an external
+PHY. Other peripherals that use PHY include Wireless LAN, Ethernet,
+SATA etc.
+
+The intention of creating this framework is to bring the PHY drivers spread
+all over the Linux kernel to drivers/phy to increase code re-use and for
+better code maintainability.
+
+This framework will be of use only to devices that use external PHY (PHY
+functionality is not embedded within the controller).
+
+2. Registering/Unregistering the PHY provider
+
+PHY provider refers to an entity that implements one or more PHY instances.
+For the simple case where the PHY provider implements only a single instance of
+the PHY, the framework provides its own implementation of of_xlate in
+of_phy_simple_xlate. If the PHY provider implements multiple instances, it
+should provide its own implementation of of_xlate. of_xlate is used only for
+dt boot case.
+
+#define of_phy_provider_register(dev, xlate)    \
+        __of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+#define devm_of_phy_provider_register(dev, xlate)       \
+        __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+of_phy_provider_register and devm_of_phy_provider_register macros can be used to
+register the phy_provider and it takes device and of_xlate as
+arguments. For the dt boot case, all PHY providers should use one of the above
+2 macros to register the PHY provider.
+
+void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider);
+void of_phy_provider_unregister(struct phy_provider *phy_provider);
+
+devm_of_phy_provider_unregister and of_phy_provider_unregister can be used to
+unregister the PHY.
+
+3. Creating the PHY
+
+The PHY driver should create the PHY in order for other peripheral controllers
+to make use of it. The PHY framework provides 2 APIs to create the PHY.
+
+struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
+        struct phy_init_data *init_data);
+struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
+       struct phy_init_data *init_data);
+
+The PHY drivers can use one of the above 2 APIs to create the PHY by passing
+the device pointer, phy ops and init_data.
+phy_ops is a set of function pointers for performing PHY operations such as
+init, exit, power_on and power_off. *init_data* is mandatory to get a reference
+to the PHY in the case of non-dt boot. See section *Board File Initialization*
+on how init_data should be used.
+
+Inorder to dereference the private data (in phy_ops), the phy provider driver
+can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in
+phy_ops to get back the private data.
+
+4. Getting a reference to the PHY
+
+Before the controller can make use of the PHY, it has to get a reference to
+it. This framework provides the following APIs to get a reference to the PHY.
+
+struct phy *phy_get(struct device *dev, const char *string);
+struct phy *devm_phy_get(struct device *dev, const char *string);
+
+phy_get and devm_phy_get can be used to get the PHY. In the case of dt boot,
+the string arguments should contain the phy name as given in the dt data and
+in the case of non-dt boot, it should contain the label of the PHY.
+The only difference between the two APIs is that devm_phy_get associates the
+device with the PHY using devres on successful PHY get. On driver detach,
+release function is invoked on the the devres data and devres data is freed.
+
+5. Releasing a reference to the PHY
+
+When the controller no longer needs the PHY, it has to release the reference
+to the PHY it has obtained using the APIs mentioned in the above section. The
+PHY framework provides 2 APIs to release a reference to the PHY.
+
+void phy_put(struct phy *phy);
+void devm_phy_put(struct device *dev, struct phy *phy);
+
+Both these APIs are used to release a reference to the PHY and devm_phy_put
+destroys the devres associated with this PHY.
+
+6. Destroying the PHY
+
+When the driver that created the PHY is unloaded, it should destroy the PHY it
+created using one of the following 2 APIs.
+
+void phy_destroy(struct phy *phy);
+void devm_phy_destroy(struct device *dev, struct phy *phy);
+
+Both these APIs destroy the PHY and devm_phy_destroy destroys the devres
+associated with this PHY.
+
+7. PM Runtime
+
+This subsystem is pm runtime enabled. So while creating the PHY,
+pm_runtime_enable of the phy device created by this subsystem is called and
+while destroying the PHY, pm_runtime_disable is called. Note that the phy
+device created by this subsystem will be a child of the device that calls
+phy_create (PHY provider device).
+
+So pm_runtime_get_sync of the phy_device created by this subsystem will invoke
+pm_runtime_get_sync of PHY provider device because of parent-child relationship.
+It should also be noted that phy_power_on and phy_power_off performs
+phy_pm_runtime_get_sync and phy_pm_runtime_put respectively.
+There are exported APIs like phy_pm_runtime_get, phy_pm_runtime_get_sync,
+phy_pm_runtime_put, phy_pm_runtime_put_sync, phy_pm_runtime_allow and
+phy_pm_runtime_forbid for performing PM operations.
+
+8. Board File Initialization
+
+Certain board file initialization is necessary in order to get a reference
+to the PHY in the case of non-dt boot.
+Say we have a single device that implements 3 PHYs that of USB, SATA and PCIe,
+then in the board file the following initialization should be done.
+
+struct phy_consumer consumers[] = {
+       PHY_CONSUMER("dwc3.0", "usb"),
+       PHY_CONSUMER("pcie.0", "pcie"),
+       PHY_CONSUMER("sata.0", "sata"),
+};
+PHY_CONSUMER takes 2 parameters, first is the device name of the controller
+(PHY consumer) and second is the port name.
+
+struct phy_init_data init_data = {
+       .consumers = consumers,
+       .num_consumers = ARRAY_SIZE(consumers),
+};
+
+static const struct platform_device pipe3_phy_dev = {
+       .name = "pipe3-phy",
+       .id = -1,
+       .dev = {
+               .platform_data = {
+                       .init_data = &init_data,
+               },
+       },
+};
+
+then, while doing phy_create, the PHY driver should pass this init_data
+       phy_create(dev, ops, pdata->init_data);
+
+and the controller driver (phy consumer) should pass the port name along with
+the device to get a reference to the PHY
+       phy_get(dev, "pcie");
+
+9. DeviceTree Binding
+
+The documentation for PHY dt binding can be found @
+Documentation/devicetree/bindings/phy/phy-bindings.txt
index d35dcdd82ff6a6c93b2dae00be717080b9b21cb1..c03b1be5eb1517b7b16cbddd6d2999101020ca53 100644 (file)
@@ -66,6 +66,21 @@ In LinuxPPS the PPS sources are simply char devices usually mapped
 into files /dev/pps0, /dev/pps1, etc..
 
 
+PPS with USB to serial devices
+------------------------------
+
+It is possible to grab the PPS from an USB to serial device. However,
+you should take into account the latencies and jitter introduced by
+the USB stack. Users has reported clock instability around +-1ms when
+synchronized with PPS through USB. This isn't suited for time server
+synchronization.
+
+If your device doesn't report PPS, you can check that the feature is
+supported by its driver. Most of the time, you only need to add a call
+to usb_serial_handle_dcd_change after checking the DCD status (see
+ch341 and pl2303 examples).
+
+
 Coding example
 --------------
 
index 067c47d4691747757b9b0a36efc37d95e79df1e3..c3a7689a90e69260aa1ca76910db8a1aaa258662 100644 (file)
@@ -264,10 +264,6 @@ hardware.
        Locking: none.
        Interrupts: caller dependent.
 
-  set_wake(port,state)
-       Enable/disable power management wakeup on serial activity.  Not
-       currently implemented.
-
   type(port)
        Return a pointer to a string constant describing the specified
        port, or return NULL, in which case the string 'unknown' is
index 8cb4d7842a5ffa001eb4c0e56bed1198ffdb8d96..0e307c94809a02586d59c13ca8c56985b632cfaf 100644 (file)
@@ -11,27 +11,29 @@ regardless of whatever else it is doing, unless it is completely locked up.
 You need to say "yes" to 'Magic SysRq key (CONFIG_MAGIC_SYSRQ)' when
 configuring the kernel. When running a kernel with SysRq compiled in,
 /proc/sys/kernel/sysrq controls the functions allowed to be invoked via
-the SysRq key. By default the file contains 1 which means that every
-possible SysRq request is allowed (in older versions SysRq was disabled
-by default, and you were required to specifically enable it at run-time
-but this is not the case any more). Here is the list of possible values
-in /proc/sys/kernel/sysrq:
+the SysRq key. The default value in this file is set by the
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE config symbol, which itself defaults
+to 1. Here is the list of possible values in /proc/sys/kernel/sysrq:
    0 - disable sysrq completely
    1 - enable all functions of sysrq
   >1 - bitmask of allowed sysrq functions (see below for detailed function
        description):
-          2 - enable control of console logging level
-          4 - enable control of keyboard (SAK, unraw)
-          8 - enable debugging dumps of processes etc.
-         16 - enable sync command
-         32 - enable remount read-only
-         64 - enable signalling of processes (term, kill, oom-kill)
-        128 - allow reboot/poweroff
-        256 - allow nicing of all RT tasks
+          2 =   0x2 - enable control of console logging level
+          4 =   0x4 - enable control of keyboard (SAK, unraw)
+          8 =   0x8 - enable debugging dumps of processes etc.
+         16 =  0x10 - enable sync command
+         32 =  0x20 - enable remount read-only
+         64 =  0x40 - enable signalling of processes (term, kill, oom-kill)
+        128 =  0x80 - allow reboot/poweroff
+        256 = 0x100 - allow nicing of all RT tasks
 
 You can set the value in the file by the following command:
     echo "number" >/proc/sys/kernel/sysrq
 
+The number may be written here either as decimal or as hexadecimal
+with the 0x prefix. CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE must always be
+written in hexadecimal.
+
 Note that the value of /proc/sys/kernel/sysrq influences only the invocation
 via a keyboard. Invocation of any operation via /proc/sysrq-trigger is always
 allowed (by a user with admin privileges).
index 57508c20eafc1a8ede45fbd9455c4791f96e2909..831b8690cf136826ac34273edb77ecb61cbdc3d6 100644 (file)
@@ -1009,6 +1009,7 @@ ARM/Marvell Armada 370 and Armada XP SOC support
 M:     Jason Cooper <jason@lakedaemon.net>
 M:     Andrew Lunn <andrew@lunn.ch>
 M:     Gregory Clement <gregory.clement@free-electrons.com>
+M:     Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     arch/arm/mach-mvebu/
@@ -1016,6 +1017,7 @@ F:        arch/arm/mach-mvebu/
 ARM/Marvell Dove/Kirkwood/MV78xx0/Orion SOC support
 M:     Jason Cooper <jason@lakedaemon.net>
 M:     Andrew Lunn <andrew@lunn.ch>
+M:     Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     arch/arm/mach-dove/
@@ -1148,6 +1150,13 @@ F:       drivers/net/ethernet/i825xx/ether1*
 F:     drivers/net/ethernet/seeq/ether3*
 F:     drivers/scsi/arm/
 
+ARM/Rockchip SoC support
+M:     Heiko Stuebner <heiko@sntech.de>
+L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:     Maintained
+F:     arch/arm/mach-rockchip/
+F:     drivers/*/*rockchip*
+
 ARM/SHARK MACHINE SUPPORT
 M:     Alexander Schulz <alex@shark-linux.de>
 W:     http://www.shark-linux.de/shark.html
@@ -1791,6 +1800,7 @@ F:        include/net/bluetooth/
 
 BONDING DRIVER
 M:     Jay Vosburgh <fubar@us.ibm.com>
+M:     Veaceslav Falico <vfalico@redhat.com>
 M:     Andy Gospodarek <andy@greyhouse.net>
 L:     netdev@vger.kernel.org
 W:     http://sourceforge.net/projects/bonding/
@@ -2718,6 +2728,8 @@ T:        git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git
 DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
 M:     Vinod Koul <vinod.koul@intel.com>
 M:     Dan Williams <dan.j.williams@intel.com>
+L:     dmaengine@vger.kernel.org
+Q:     https://patchwork.kernel.org/project/linux-dmaengine/list/
 S:     Supported
 F:     drivers/dma/
 F:     include/linux/dma*
@@ -2821,7 +2833,7 @@ M:        Terje Bergström <tbergstrom@nvidia.com>
 L:     dri-devel@lists.freedesktop.org
 L:     linux-tegra@vger.kernel.org
 T:     git git://anongit.freedesktop.org/tegra/linux.git
-S:     Maintained
+S:     Supported
 F:     drivers/gpu/host1x/
 F:     include/uapi/drm/tegra_drm.h
 F:     Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
@@ -3679,6 +3691,14 @@ S:       Maintained
 F:     include/asm-generic/
 F:     include/uapi/asm-generic/
 
+GENERIC PHY FRAMEWORK
+M:     Kishon Vijay Abraham I <kishon@ti.com>
+L:     linux-kernel@vger.kernel.org
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy.git
+S:     Supported
+F:     drivers/phy/
+F:     include/linux/phy/
+
 GENERIC UIO DRIVER FOR PCI DEVICES
 M:     "Michael S. Tsirkin" <mst@redhat.com>
 L:     kvm@vger.kernel.org
@@ -4357,7 +4377,10 @@ F:       arch/x86/kernel/microcode_intel.c
 
 INTEL I/OAT DMA DRIVER
 M:     Dan Williams <dan.j.williams@intel.com>
-S:     Maintained
+M:     Dave Jiang <dave.jiang@intel.com>
+L:     dmaengine@vger.kernel.org
+Q:     https://patchwork.kernel.org/project/linux-dmaengine/list/
+S:     Supported
 F:     drivers/dma/ioat*
 
 INTEL IOMMU (VT-d)
@@ -7829,6 +7852,13 @@ F:       Documentation/sound/alsa/soc/
 F:     sound/soc/
 F:     include/sound/soc*
 
+SOUND - DMAENGINE HELPERS
+M:     Lars-Peter Clausen <lars@metafoo.de>
+S:     Supported
+F:     include/sound/dmaengine_pcm.h
+F:     sound/core/pcm_dmaengine.c
+F:     sound/soc/soc-generic-dmaengine-pcm.c
+
 SPARC + UltraSPARC (sparc/sparc64)
 M:     "David S. Miller" <davem@davemloft.net>
 L:     sparclinux@vger.kernel.org
@@ -8301,14 +8331,72 @@ L:      linux-media@vger.kernel.org
 S:     Maintained
 F:     drivers/media/rc/ttusbir.c
 
-TEGRA SUPPORT
+TEGRA ARCHITECTURE SUPPORT
 M:     Stephen Warren <swarren@wwwdotorg.org>
+M:     Thierry Reding <thierry.reding@gmail.com>
 L:     linux-tegra@vger.kernel.org
 Q:     http://patchwork.ozlabs.org/project/linux-tegra/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra.git
 S:     Supported
 N:     [^a-z]tegra
 
+TEGRA ASOC DRIVER
+M:     Stephen Warren <swarren@wwwdotorg.org>
+S:     Supported
+F:     sound/soc/tegra/
+
+TEGRA CLOCK DRIVER
+M:     Peter De Schrijver <pdeschrijver@nvidia.com>
+M:     Prashant Gaikwad <pgaikwad@nvidia.com>
+S:     Supported
+F:     drivers/clk/tegra/
+
+TEGRA DMA DRIVER
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/dma/tegra20-apb-dma.c
+
+TEGRA GPIO DRIVER
+M:     Stephen Warren <swarren@wwwdotorg.org>
+S:     Supported
+F:     drivers/gpio/gpio-tegra.c
+
+TEGRA I2C DRIVER
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/i2c/busses/i2c-tegra.c
+
+TEGRA IOMMU DRIVERS
+M:     Hiroshi Doyu <hdoyu@nvidia.com>
+S:     Supported
+F:     drivers/iommu/tegra*
+
+TEGRA KBC DRIVER
+M:     Rakesh Iyer <riyer@nvidia.com>
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/input/keyboard/tegra-kbc.c
+
+TEGRA PINCTRL DRIVER
+M:     Stephen Warren <swarren@wwwdotorg.org>
+S:     Supported
+F:     drivers/pinctrl/pinctrl-tegra*
+
+TEGRA PWM DRIVER
+M:     Thierry Reding <thierry.reding@gmail.com>
+S:     Supported
+F:     drivers/pwm/pwm-tegra.c
+
+TEGRA SERIAL DRIVER
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/tty/serial/serial-tegra.c
+
+TEGRA SPI DRIVER
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/spi/spi-tegra*
+
 TEHUTI ETHERNET DRIVER
 M:     Andy Gospodarek <andy@greyhouse.net>
 L:     netdev@vger.kernel.org
@@ -8844,61 +8932,14 @@ W:      http://pegasus2.sourceforge.net/
 S:     Maintained
 F:     drivers/net/usb/rtl8150.c
 
-USB SERIAL BELKIN F5U103 DRIVER
-M:     William Greathouse <wgreathouse@smva.com>
-L:     linux-usb@vger.kernel.org
-S:     Maintained
-F:     drivers/usb/serial/belkin_sa.*
-
-USB SERIAL CYPRESS M8 DRIVER
-M:     Lonnie Mendez <dignome@gmail.com>
+USB SERIAL SUBSYSTEM
+M:     Johan Hovold <jhovold@gmail.com>
 L:     linux-usb@vger.kernel.org
 S:     Maintained
-W:     http://geocities.com/i0xox0i
-W:     http://firstlight.net/cvs
-F:     drivers/usb/serial/cypress_m8.*
-
-USB SERIAL CYBERJACK DRIVER
-M:     Matthias Bruestle and Harald Welte <support@reiner-sct.com>
-W:     http://www.reiner-sct.de/support/treiber_cyberjack.php
-S:     Maintained
-F:     drivers/usb/serial/cyberjack.c
-
-USB SERIAL DIGI ACCELEPORT DRIVER
-M:     Peter Berger <pberger@brimson.com>
-M:     Al Borchers <alborchers@steinerpoint.com>
-L:     linux-usb@vger.kernel.org
-S:     Maintained
-F:     drivers/usb/serial/digi_acceleport.c
-
-USB SERIAL DRIVER
-M:     Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-L:     linux-usb@vger.kernel.org
-S:     Supported
 F:     Documentation/usb/usb-serial.txt
-F:     drivers/usb/serial/generic.c
-F:     drivers/usb/serial/usb-serial.c
+F:     drivers/usb/serial/
 F:     include/linux/usb/serial.h
 
-USB SERIAL EMPEG EMPEG-CAR MARK I/II DRIVER
-M:     Gary Brubaker <xavyer@ix.netcom.com>
-L:     linux-usb@vger.kernel.org
-S:     Maintained
-F:     drivers/usb/serial/empeg.c
-
-USB SERIAL KEYSPAN DRIVER
-M:     Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-L:     linux-usb@vger.kernel.org
-S:     Maintained
-F:     drivers/usb/serial/*keyspan*
-
-USB SERIAL WHITEHEAT DRIVER
-M:     Support Department <support@connecttech.com>
-L:     linux-usb@vger.kernel.org
-W:     http://www.connecttech.com
-S:     Supported
-F:     drivers/usb/serial/whiteheat*
-
 USB SMSC75XX ETHERNET DRIVER
 M:     Steve Glendinning <steve.glendinning@shawell.net>
 L:     netdev@vger.kernel.org
index 126321d2e6ad6a3c594280ca1102e0b25c7e4dd3..67077ad6edbb26cbe7ea9b54efbc7ad51105548a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 3
 PATCHLEVEL = 12
 SUBLEVEL = 0
-EXTRAVERSION = -rc6
+EXTRAVERSION =
 NAME = One Giant Leap for Frogkind
 
 # *DOCUMENTATION*
index d63f3de0cd5bf60e209bf00be68fb8c628862126..0c14d8a526833fd3236f680abcad6c5f63279d36 100644 (file)
@@ -17,7 +17,7 @@
 #include <asm/pgalloc.h>
 #include <asm/mmu.h>
 
-static int handle_vmalloc_fault(struct mm_struct *mm, unsigned long address)
+static int handle_vmalloc_fault(unsigned long address)
 {
        /*
         * Synchronize this task's top level page-table
@@ -27,7 +27,7 @@ static int handle_vmalloc_fault(struct mm_struct *mm, unsigned long address)
        pud_t *pud, *pud_k;
        pmd_t *pmd, *pmd_k;
 
-       pgd = pgd_offset_fast(mm, address);
+       pgd = pgd_offset_fast(current->active_mm, address);
        pgd_k = pgd_offset_k(address);
 
        if (!pgd_present(*pgd_k))
@@ -72,7 +72,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address)
         * nothing more.
         */
        if (address >= VMALLOC_START && address <= VMALLOC_END) {
-               ret = handle_vmalloc_fault(mm, address);
+               ret = handle_vmalloc_fault(address);
                if (unlikely(ret))
                        goto bad_area_nosemaphore;
                else
index ff1aea0ee04322bf26688e1bf8892b60b02a69ce..72693a69f8300421f687f404ffa9f95768ea33d3 100644 (file)
@@ -9,11 +9,6 @@
        model = "ARM Integrator/CP";
        compatible = "arm,integrator-cp";
 
-       aliases {
-               arm,timer-primary = &timer2;
-               arm,timer-secondary = &timer1;
-       };
-
        chosen {
                bootargs = "root=/dev/ram0 console=ttyAMA0,38400n8 earlyprintk";
        };
        };
 
        timer0: timer@13000000 {
+               /* TIMER0 runs @ 25MHz */
                compatible = "arm,integrator-cp-timer";
+               status = "disabled";
        };
 
        timer1: timer@13000100 {
+               /* TIMER1 runs @ 1MHz */
                compatible = "arm,integrator-cp-timer";
        };
 
        timer2: timer@13000200 {
+               /* TIMER2 runs @ 1MHz */
                compatible = "arm,integrator-cp-timer";
        };
 
index 2816bf61267231fd7ec2dd11f87c6da9ed4b3311..ba4dcfc6b7214d5e3d63488edc2e8157f130ac19 100644 (file)
 &usb_otg_hs {
        interface-type = <0>;
        usb-phy = <&usb2_phy>;
+       phys = <&usb2_phy>;
+       phy-names = "usb2-phy";
        mode = <3>;
        power = <50>;
 };
index 7d4329d179c43e24cae9bfaddb4c5b1fbc626535..4134dd05c3a43835e56963aebb25a18347701588 100644 (file)
@@ -70,6 +70,8 @@
 &usb_otg_hs {
        interface-type = <0>;
        usb-phy = <&usb2_phy>;
+       phys = <&usb2_phy>;
+       phy-names = "usb2-phy";
        mode = <3>;
        power = <50>;
 };
index 8f1abec78275635535f9faf4cdd03c60d2daf909..a461d2fd1fb0e81862805ccec939e1c1b3a41244 100644 (file)
@@ -76,6 +76,8 @@
 &usb_otg_hs {
        interface-type = <0>;
        usb-phy = <&usb2_phy>;
+       phys = <&usb2_phy>;
+       phy-names = "usb2-phy";
        mode = <3>;
        power = <50>;
 };
index 22d9f2b593d461eb5bde36a35f24e2eaea40e298..ea4054bfdfd4964c57573813de9306f5f538c034 100644 (file)
                        usb2_phy: usb2phy@4a0ad080 {
                                compatible = "ti,omap-usb2";
                                reg = <0x4a0ad080 0x58>;
-                               ctrl-module = <&omap_control_usb>;
+                               ctrl-module = <&omap_control_usb2phy>;
+                               #phy-cells = <0>;
                        };
                };
 
                        };
                };
 
-               omap_control_usb: omap-control-usb@4a002300 {
-                       compatible = "ti,omap-control-usb";
-                       reg = <0x4a002300 0x4>,
-                             <0x4a00233c 0x4>;
-                       reg-names = "control_dev_conf", "otghs_control";
-                       ti,type = <1>;
+               omap_control_usb2phy: control-phy@4a002300 {
+                       compatible = "ti,control-phy-usb2";
+                       reg = <0x4a002300 0x4>;
+                       reg-names = "power";
+               };
+
+               omap_control_usbotg: control-phy@4a00233c {
+                       compatible = "ti,control-phy-otghs";
+                       reg = <0x4a00233c 0x4>;
+                       reg-names = "otghs_control";
                };
 
                usb_otg_hs: usb_otg_hs@4a0ab000 {
                        interrupt-names = "mc", "dma";
                        ti,hwmods = "usb_otg_hs";
                        usb-phy = <&usb2_phy>;
+                       phys = <&usb2_phy>;
+                       phy-names = "usb2-phy";
                        multipoint = <1>;
                        num-eps = <16>;
                        ram-bits = <12>;
-                       ti,has-mailbox;
+                       ctrl-module = <&omap_control_usbotg>;
                };
        };
 };
index 7cdea1bfea091917455e46cc18af7d6d80a2c0f0..c0ec6dce30fe2dd3e18e799fb1ce0f82f8a17c81 100644 (file)
                        hw-caps-temp-alert;
                };
 
-               omap_control_usb: omap-control-usb@4a002300 {
-                       compatible = "ti,omap-control-usb";
-                       reg = <0x4a002300 0x4>,
-                             <0x4a002370 0x4>;
-                       reg-names = "control_dev_conf", "phy_power_usb";
-                       ti,type = <2>;
+               omap_control_usb2phy: control-phy@4a002300 {
+                       compatible = "ti,control-phy-usb2";
+                       reg = <0x4a002300 0x4>;
+                       reg-names = "power";
+               };
+
+               omap_control_usb3phy: control-phy@4a002370 {
+                       compatible = "ti,control-phy-pipe3";
+                       reg = <0x4a002370 0x4>;
+                       reg-names = "power";
                };
 
                omap_dwc3@4a020000 {
                        usb2_phy: usb2phy@4a084000 {
                                compatible = "ti,omap-usb2";
                                reg = <0x4a084000 0x7c>;
-                               ctrl-module = <&omap_control_usb>;
+                               ctrl-module = <&omap_control_usb2phy>;
                        };
 
                        usb3_phy: usb3phy@4a084400 {
                                      <0x4a084800 0x64>,
                                      <0x4a084c00 0x40>;
                                reg-names = "phy_rx", "phy_tx", "pll_ctrl";
-                               ctrl-module = <&omap_control_usb>;
+                               ctrl-module = <&omap_control_usb3phy>;
                        };
                };
 
index ae6a17aed9ee224c85498b2bc6b1e0fa43e75e56..5aba238d1f1ec5322eb817e0eb606dffdd763b65 100644 (file)
@@ -86,6 +86,7 @@
                usb1v8-supply = <&vusb1v8>;
                usb3v1-supply = <&vusb3v1>;
                usb_mode = <1>;
+               #phy-cells = <0>;
        };
 
        twl_pwm: pwm {
index 806005a4c4c15cdeac19cbc6802494df0ff4e982..6ac5ea73bd0a6852082187e38e6b02d4efdb9a59 100644 (file)
@@ -1,15 +1,14 @@
-CONFIG_EXPERIMENTAL=y
 CONFIG_SYSVIPC=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=14
-CONFIG_SYSFS_DEPRECATED_V2=y
 CONFIG_EXPERT=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
 # CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
 # CONFIG_IOSCHED_CFQ is not set
 CONFIG_ARCH_EP93XX=y
 CONFIG_CRUNCH=y
@@ -47,11 +46,8 @@ CONFIG_IPV6=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CONCAT=y
-CONFIG_MTD_PARTITIONS=y
 CONFIG_MTD_REDBOOT_PARTS=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_ADV_OPTIONS=y
@@ -67,15 +63,14 @@ CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
 CONFIG_BLK_DEV_SD=y
 CONFIG_NETDEVICES=y
-CONFIG_NET_ETHERNET=y
 CONFIG_EP93XX_ETH=y
 CONFIG_USB_RTL8150=y
 # CONFIG_INPUT is not set
 # CONFIG_SERIO is not set
 # CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
 CONFIG_SERIAL_AMBA_PL010=y
 CONFIG_SERIAL_AMBA_PL010_CONSOLE=y
-# CONFIG_LEGACY_PTYS is not set
 # CONFIG_HW_RANDOM is not set
 CONFIG_I2C=y
 CONFIG_I2C_CHARDEV=y
@@ -86,9 +81,9 @@ CONFIG_WATCHDOG=y
 CONFIG_EP93XX_WATCHDOG=y
 CONFIG_USB=y
 CONFIG_USB_DEBUG=y
-CONFIG_USB_DEVICEFS=y
 CONFIG_USB_DYNAMIC_MINORS=y
 CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
 CONFIG_USB_STORAGE=y
 CONFIG_USB_SERIAL=y
 CONFIG_USB_SERIAL_CONSOLE=y
@@ -100,24 +95,18 @@ CONFIG_RTC_DRV_EP93XX=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 # CONFIG_EXT3_FS_XATTR is not set
-CONFIG_INOTIFY=y
 CONFIG_VFAT_FS=y
 CONFIG_TMPFS=y
 CONFIG_JFFS2_FS=y
 CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
 CONFIG_ROOT_NFS=y
-CONFIG_PARTITION_ADVANCED=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_KERNEL=y
 CONFIG_DEBUG_SLAB=y
 CONFIG_DEBUG_SPINLOCK=y
 CONFIG_DEBUG_MUTEXES=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
 CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_ERRORS=y
 CONFIG_DEBUG_LL=y
 # CONFIG_CRYPTO_ANSI_CPRNG is not set
 CONFIG_LIBCRC32C=y
index c95dbce2468e62010c8df64e3be4751869bd7abc..39ef3b613912f8888013c30d591e4ad4f0cd8095 100644 (file)
@@ -212,7 +212,7 @@ static struct clk_lookup clocks[] = {
        INIT_CK(NULL,                   "hclk",         &clk_h),
        INIT_CK(NULL,                   "apb_pclk",     &clk_p),
        INIT_CK(NULL,                   "pll2",         &clk_pll2),
-       INIT_CK("ep93xx-ohci",          NULL,           &clk_usb_host),
+       INIT_CK("ohci-platform",        NULL,           &clk_usb_host),
        INIT_CK("ep93xx-keypad",        NULL,           &clk_keypad),
        INIT_CK("ep93xx-fb",            NULL,           &clk_video),
        INIT_CK("ep93xx-spi.0",         NULL,           &clk_spi),
index 3f12b885c083ac37424bdb9e44ca478f09a3c12b..d95ee28a616a3ed53c776dda2df2fa5da6f229f1 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/export.h>
 #include <linux/irqchip/arm-vic.h>
 #include <linux/reboot.h>
+#include <linux/usb/ohci_pdriver.h>
 
 #include <mach/hardware.h>
 #include <linux/platform_data/video-ep93xx.h>
@@ -297,25 +298,53 @@ static struct platform_device ep93xx_rtc_device = {
        .resource       = ep93xx_rtc_resource,
 };
 
+/*************************************************************************
+ * EP93xx OHCI USB Host
+ *************************************************************************/
+
+static struct clk *ep93xx_ohci_host_clock;
+
+static int ep93xx_ohci_power_on(struct platform_device *pdev)
+{
+       if (!ep93xx_ohci_host_clock) {
+               ep93xx_ohci_host_clock = devm_clk_get(&pdev->dev, NULL);
+               if (IS_ERR(ep93xx_ohci_host_clock))
+                       return PTR_ERR(ep93xx_ohci_host_clock);
+       }
+
+       return clk_enable(ep93xx_ohci_host_clock);
+}
+
+static void ep93xx_ohci_power_off(struct platform_device *pdev)
+{
+       clk_disable(ep93xx_ohci_host_clock);
+}
+
+static struct usb_ohci_pdata ep93xx_ohci_pdata = {
+       .power_on       = ep93xx_ohci_power_on,
+       .power_off      = ep93xx_ohci_power_off,
+       .power_suspend  = ep93xx_ohci_power_off,
+};
 
 static struct resource ep93xx_ohci_resources[] = {
        DEFINE_RES_MEM(EP93XX_USB_PHYS_BASE, 0x1000),
        DEFINE_RES_IRQ(IRQ_EP93XX_USB),
 };
 
+static u64 ep93xx_ohci_dma_mask = DMA_BIT_MASK(32);
 
 static struct platform_device ep93xx_ohci_device = {
-       .name           = "ep93xx-ohci",
+       .name           = "ohci-platform",
        .id             = -1,
+       .num_resources  = ARRAY_SIZE(ep93xx_ohci_resources),
+       .resource       = ep93xx_ohci_resources,
        .dev            = {
-               .dma_mask               = &ep93xx_ohci_device.dev.coherent_dma_mask,
+               .dma_mask               = &ep93xx_ohci_dma_mask,
                .coherent_dma_mask      = DMA_BIT_MASK(32),
+               .platform_data          = &ep93xx_ohci_pdata,
        },
-       .num_resources  = ARRAY_SIZE(ep93xx_ohci_resources),
-       .resource       = ep93xx_ohci_resources,
 };
 
-
 /*************************************************************************
  * EP93xx physmap'ed flash
  *************************************************************************/
index 57344b7e98ce03b8a46dc1a3698db60ef83b6dd9..2cdb63e8ce5c0eb352064f67c7c4d8dae8b43541 100644 (file)
 #define S5P_DAC_PHY_CONTROL                    S5P_PMUREG(0x070C)
 #define S5P_DAC_PHY_ENABLE                     (1 << 0)
 
-#define S5P_MIPI_DPHY_CONTROL(n)               S5P_PMUREG(0x0710 + (n) * 4)
-#define S5P_MIPI_DPHY_ENABLE                   (1 << 0)
-#define S5P_MIPI_DPHY_SRESETN                  (1 << 1)
-#define S5P_MIPI_DPHY_MRESETN                  (1 << 2)
-
 #define S5P_INFORM0                            S5P_PMUREG(0x0800)
 #define S5P_INFORM1                            S5P_PMUREG(0x0804)
 #define S5P_INFORM2                            S5P_PMUREG(0x0808)
index f26918467efcf42cc13639317965ebdb6d34bf14..8b9cd0690ce79328d12c664e1ef2ef301ef0b7eb 100644 (file)
@@ -289,18 +289,12 @@ static struct regulator_consumer_supply beagle_vsim_supply[] = {
 
 static struct gpio_led gpio_leds[];
 
-/* PHY's VCC regulator might be added later, so flag that we need it */
-static struct usb_phy_gen_xceiv_platform_data hsusb2_phy_data = {
-       .needs_vcc = true,
-};
-
 static struct usbhs_phy_data phy_data[] = {
        {
                .port = 2,
                .reset_gpio = 147,
                .vcc_gpio = -1,         /* updated in beagle_twl_gpio_setup */
                .vcc_polarity = 1,      /* updated in beagle_twl_gpio_setup */
-               .platform_data = &hsusb2_phy_data,
        },
 };
 
index c05898fbd634a813542ab0c0b6e8b86586a6a0ee..b0d54dae1bcb536d818df84ad7642ba123a65ef5 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/i2c/twl.h>
 #include <linux/gpio.h>
 #include <linux/string.h>
+#include <linux/phy/phy.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/fixed.h>
 
@@ -90,8 +91,18 @@ void __init omap_pmic_late_init(void)
 }
 
 #if defined(CONFIG_ARCH_OMAP3)
+struct phy_consumer consumers[] = {
+       PHY_CONSUMER("musb-hdrc.0", "usb"),
+};
+
+struct phy_init_data init_data = {
+       .consumers = consumers,
+       .num_consumers = ARRAY_SIZE(consumers),
+};
+
 static struct twl4030_usb_data omap3_usb_pdata = {
        .usb_mode       = T2_USB_MODE_ULPI,
+       .init_data      = &init_data,
 };
 
 static int omap3_batt_table[] = {
index e83a6a4b184af23c315a5e509a56ba8362d820e7..10855eb4ccc1c7ed8bd712a22cf418f7265d6567 100644 (file)
@@ -435,6 +435,7 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys)
        struct platform_device *pdev;
        char *phy_id;
        struct platform_device_info pdevinfo;
+       struct usb_phy_gen_xceiv_platform_data nop_pdata;
 
        for (i = 0; i < num_phys; i++) {
 
@@ -455,11 +456,18 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys)
                        return -ENOMEM;
                }
 
+               /* set platform data */
+               memset(&nop_pdata, 0, sizeof(nop_pdata));
+               if (gpio_is_valid(phy->vcc_gpio))
+                       nop_pdata.needs_vcc = true;
+               nop_pdata.gpio_reset = phy->reset_gpio;
+               nop_pdata.type = USB_PHY_TYPE_USB2;
+
                /* create a NOP PHY device */
                memset(&pdevinfo, 0, sizeof(pdevinfo));
                pdevinfo.name = nop_name;
                pdevinfo.id = phy->port;
-               pdevinfo.data = phy->platform_data;
+               pdevinfo.data = &nop_pdata;
                pdevinfo.size_data =
                        sizeof(struct usb_phy_gen_xceiv_platform_data);
                scnprintf(phy_id, MAX_STR, "usb_phy_gen_xceiv.%d",
@@ -474,14 +482,6 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys)
 
                usb_bind_phy("ehci-omap.0", phy->port - 1, phy_id);
 
-               /* Do we need RESET regulator ? */
-               if (gpio_is_valid(phy->reset_gpio)) {
-                       scnprintf(rail_name, MAX_STR,
-                                       "hsusb%d_reset", phy->port);
-                       usbhs_add_regulator(rail_name, phy_id, "reset",
-                                               phy->reset_gpio, 1);
-               }
-
                /* Do we need VCC regulator ? */
                if (gpio_is_valid(phy->vcc_gpio)) {
                        scnprintf(rail_name, MAX_STR, "hsusb%d_vcc", phy->port);
index e7261ebcf7b02ba83c4e4fe672d132636aeb841e..4ba2ae759895a1e7db30edc440560cacf3f1b1c0 100644 (file)
@@ -58,7 +58,6 @@ struct usbhs_phy_data {
        int reset_gpio;
        int vcc_gpio;
        bool vcc_polarity;      /* 1 active high, 0 active low */
-       void *platform_data;
 };
 
 extern void usb_musb_init(struct omap_musb_board_data *board_data);
index 032de66fb8be2817496da9c431a2f5fef69d66f9..e345584d4c34ccbd8363680038ad1511adf04b2a 100644 (file)
 #define S5P_HDMI_PHY_CONTROL   S5P_CLKREG(0xE804)
 #define S5P_USB_PHY_CONTROL    S5P_CLKREG(0xE80C)
 #define S5P_DAC_PHY_CONTROL    S5P_CLKREG(0xE810)
-#define S5P_MIPI_DPHY_CONTROL(x) S5P_CLKREG(0xE814)
-#define S5P_MIPI_DPHY_ENABLE   (1 << 0)
-#define S5P_MIPI_DPHY_SRESETN  (1 << 1)
-#define S5P_MIPI_DPHY_MRESETN  (1 << 2)
 
 #define S5P_INFORM0            S5P_CLKREG(0xF000)
 #define S5P_INFORM1            S5P_CLKREG(0xF004)
index f50d223a0bd31271ed73beabd60679c137ccb440..99b44e0e8d866983dd60d4b8324bf29010679e77 100644 (file)
@@ -930,4 +930,5 @@ void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter)
                module_free(NULL, fp->bpf_func);
+       kfree(fp);
 }
index 7dfba937d8fc34b017bf953b6761abd7f4f5f45c..6d95d60276d623bee59f28391d499f0e213730d5 100644 (file)
@@ -382,11 +382,6 @@ config S5P_DEV_TV
        help
          Compile in platform device definition for TV interface
 
-config S5P_DEV_USB_EHCI
-       bool
-       help
-         Compile in platform device definition for USB EHCI
-
 config S3C24XX_PWM
        bool "PWM device support"
        select PWM
@@ -395,11 +390,6 @@ config S3C24XX_PWM
          Support for exporting the PWM timer blocks via the pwm device
          system
 
-config S5P_SETUP_MIPIPHY
-       bool
-       help
-         Compile in common setup code for MIPI-CSIS and MIPI-DSIM devices
-
 config S3C_SETUP_CAMIF
        bool
        help
index 498c7c23e9f4cd8088eddc1ae937bfcae733d71e..9267d29549b47bd29c2ed6b76f98bd0c8180ce51 100644 (file)
@@ -38,7 +38,6 @@ obj-$(CONFIG_S5P_DEV_UART)    += s5p-dev-uart.o
 obj-$(CONFIG_SAMSUNG_DEV_BACKLIGHT)    += dev-backlight.o
 
 obj-$(CONFIG_S3C_SETUP_CAMIF)  += setup-camif.o
-obj-$(CONFIG_S5P_SETUP_MIPIPHY)        += setup-mipiphy.o
 
 # DMA support
 
index 8ce0ac007eb96397d0c16a67fe9014d12f7d6901..25f40c9b7f629aeffa31472ef6f31187aa2162ef 100644 (file)
@@ -49,7 +49,6 @@
 #include <plat/devs.h>
 #include <plat/adc.h>
 #include <linux/platform_data/ata-samsung_cf.h>
-#include <linux/platform_data/usb-ehci-s5p.h>
 #include <plat/fb.h>
 #include <plat/fb-s3c2410.h>
 #include <plat/hdmi.h>
@@ -1359,39 +1358,6 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 }
 #endif /* CONFIG_PLAT_S3C24XX */
 
-/* USB EHCI Host Controller */
-
-#ifdef CONFIG_S5P_DEV_USB_EHCI
-static struct resource s5p_ehci_resource[] = {
-       [0] = DEFINE_RES_MEM(S5P_PA_EHCI, SZ_256),
-       [1] = DEFINE_RES_IRQ(IRQ_USB_HOST),
-};
-
-struct platform_device s5p_device_ehci = {
-       .name           = "s5p-ehci",
-       .id             = -1,
-       .num_resources  = ARRAY_SIZE(s5p_ehci_resource),
-       .resource       = s5p_ehci_resource,
-       .dev            = {
-               .dma_mask               = &samsung_device_dma_mask,
-               .coherent_dma_mask      = DMA_BIT_MASK(32),
-       }
-};
-
-void __init s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd)
-{
-       struct s5p_ehci_platdata *npd;
-
-       npd = s3c_set_platdata(pd, sizeof(struct s5p_ehci_platdata),
-                       &s5p_device_ehci);
-
-       if (!npd->phy_init)
-               npd->phy_init = s5p_usb_phy_init;
-       if (!npd->phy_exit)
-               npd->phy_exit = s5p_usb_phy_exit;
-}
-#endif /* CONFIG_S5P_DEV_USB_EHCI */
-
 /* USB HSOTG */
 
 #ifdef CONFIG_S3C_DEV_USB_HSOTG
index 0dc4ac4909b09fc9a111ff3547b9be41dd33e3cc..eece188ed18826db7a6079f4ce18041752a98918 100644 (file)
@@ -75,7 +75,6 @@ extern struct platform_device s3c_device_usb_hsotg;
 extern struct platform_device s3c_device_usb_hsudc;
 extern struct platform_device s3c_device_wdt;
 
-extern struct platform_device s5p_device_ehci;
 extern struct platform_device s5p_device_fimc0;
 extern struct platform_device s5p_device_fimc1;
 extern struct platform_device s5p_device_fimc2;
diff --git a/arch/arm/plat-samsung/setup-mipiphy.c b/arch/arm/plat-samsung/setup-mipiphy.c
deleted file mode 100644 (file)
index 66df315..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- *
- * S5P - Helper functions for MIPI-CSIS and MIPI-DSIM D-PHY control
- *
- * 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/export.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/spinlock.h>
-#include <mach/regs-clock.h>
-
-static int __s5p_mipi_phy_control(int id, bool on, u32 reset)
-{
-       static DEFINE_SPINLOCK(lock);
-       void __iomem *addr;
-       unsigned long flags;
-       u32 cfg;
-
-       id = max(0, id);
-       if (id > 1)
-               return -EINVAL;
-
-       addr = S5P_MIPI_DPHY_CONTROL(id);
-
-       spin_lock_irqsave(&lock, flags);
-
-       cfg = __raw_readl(addr);
-       cfg = on ? (cfg | reset) : (cfg & ~reset);
-       __raw_writel(cfg, addr);
-
-       if (on) {
-               cfg |= S5P_MIPI_DPHY_ENABLE;
-       } else if (!(cfg & (S5P_MIPI_DPHY_SRESETN |
-                           S5P_MIPI_DPHY_MRESETN) & ~reset)) {
-               cfg &= ~S5P_MIPI_DPHY_ENABLE;
-       }
-
-       __raw_writel(cfg, addr);
-       spin_unlock_irqrestore(&lock, flags);
-
-       return 0;
-}
-
-int s5p_csis_phy_enable(int id, bool on)
-{
-       return __s5p_mipi_phy_control(id, on, S5P_MIPI_DPHY_SRESETN);
-}
-EXPORT_SYMBOL(s5p_csis_phy_enable);
-
-int s5p_dsim_phy_enable(struct platform_device *pdev, bool on)
-{
-       return __s5p_mipi_phy_control(pdev->id, on, S5P_MIPI_DPHY_MRESETN);
-}
-EXPORT_SYMBOL(s5p_dsim_phy_enable);
index 45f1ffcf1a4b6299b181cf99e9d08cac59da6cfb..24cdf64789c39dcf8be4a4c214b02540aa02370b 100644 (file)
@@ -971,11 +971,11 @@ static const struct mips_perf_event mipsxx74Kcore_cache_map
 [C(LL)] = {
        [C(OP_READ)] = {
                [C(RESULT_ACCESS)]      = { 0x1c, CNTR_ODD, P },
-               [C(RESULT_MISS)]        = { 0x1d, CNTR_EVEN | CNTR_ODD, P },
+               [C(RESULT_MISS)]        = { 0x1d, CNTR_EVEN, P },
        },
        [C(OP_WRITE)] = {
                [C(RESULT_ACCESS)]      = { 0x1c, CNTR_ODD, P },
-               [C(RESULT_MISS)]        = { 0x1d, CNTR_EVEN | CNTR_ODD, P },
+               [C(RESULT_MISS)]        = { 0x1d, CNTR_EVEN, P },
        },
 },
 [C(ITLB)] = {
index c69da37346995e94a5e5d4a33dafce0585b84ed9..5b28e81d94a086ad8a080e727439ffb48fa22ac9 100644 (file)
@@ -473,7 +473,7 @@ static void __init fill_ipi_map(void)
 {
        int cpu;
 
-       for (cpu = 0; cpu < NR_CPUS; cpu++) {
+       for (cpu = 0; cpu < nr_cpu_ids; cpu++) {
                fill_ipi_map1(gic_resched_int_base, cpu, GIC_CPU_INT1);
                fill_ipi_map1(gic_call_int_base, cpu, GIC_CPU_INT2);
        }
@@ -574,8 +574,9 @@ void __init arch_init_irq(void)
                /* FIXME */
                int i;
 #if defined(CONFIG_MIPS_MT_SMP)
-               gic_call_int_base = GIC_NUM_INTRS - NR_CPUS;
-               gic_resched_int_base = gic_call_int_base - NR_CPUS;
+               gic_call_int_base = GIC_NUM_INTRS -
+                       (NR_CPUS - nr_cpu_ids) * 2 - nr_cpu_ids;
+               gic_resched_int_base = gic_call_int_base - nr_cpu_ids;
                fill_ipi_map();
 #endif
                gic_init(GIC_BASE_ADDR, GIC_ADDRSPACE_SZ, gic_intr_map,
@@ -599,7 +600,7 @@ void __init arch_init_irq(void)
                printk("CPU%d: status register now %08x\n", smp_processor_id(), read_c0_status());
                write_c0_status(0x1100dc00);
                printk("CPU%d: status register frc %08x\n", smp_processor_id(), read_c0_status());
-               for (i = 0; i < NR_CPUS; i++) {
+               for (i = 0; i < nr_cpu_ids; i++) {
                        arch_init_ipiirq(MIPS_GIC_IRQ_BASE +
                                         GIC_RESCHED_INT(i), &irq_resched);
                        arch_init_ipiirq(MIPS_GIC_IRQ_BASE +
index e49241a2c39a3b4d359f8d77eb6063f26ea89be0..202785709441c0a704b7cf1110e151834e9219c3 100644 (file)
@@ -126,7 +126,7 @@ static int rt_timer_probe(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       rt->membase = devm_request_and_ioremap(&pdev->dev, res);
+       rt->membase = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(rt->membase))
                return PTR_ERR(rt->membase);
 
index 0f90569b9d8546f7fa710a5b15f7883266e1ba95..9387cc2693f6a33a70459fcb46da058db395e2c4 100644 (file)
@@ -40,6 +40,8 @@ CONFIG_IP_NF_QUEUE=m
 CONFIG_LLC2=m
 CONFIG_NET_PKTGEN=m
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_PARPORT=y
index b647b182dacc17d6cf95fa79cee892fd8e0427bf..90025322b75ecd3b8a5672b76d18ce534b92ee2f 100644 (file)
@@ -79,6 +79,8 @@ CONFIG_IP_DCCP=m
 CONFIG_LLC2=m
 CONFIG_NET_PKTGEN=m
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_BLK_DEV_UMEM=m
index e289f5bf31488f0bb9eff29bb51bcbf25abd3bd1..f1a0c25bef8dc3a6667c608a092f96a39d5994aa 100644 (file)
@@ -4,6 +4,7 @@ CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
 CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
 CONFIG_MODVERSIONS=y
@@ -27,6 +28,8 @@ CONFIG_IP_PNP_BOOTP=y
 # CONFIG_INET_LRO is not set
 CONFIG_IPV6=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_PARPORT=y
 CONFIG_PARPORT_PC=y
index 311ca367b62237b87bb78023f7687ef1f7d98c85..ec1b014952b6601458f4c3b2901d8e86670b96fc 100644 (file)
@@ -5,6 +5,7 @@ CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
 CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 CONFIG_EXPERT=y
 CONFIG_KALLSYMS_ALL=y
@@ -39,6 +40,8 @@ CONFIG_NETFILTER_DEBUG=y
 CONFIG_IP_NF_QUEUE=m
 CONFIG_NET_PKTGEN=m
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_BLK_DEV_UMEM=m
index f11006361297eb152cf5f78471f6c69de41e5cfc..e1c8d2015c8938ac0a3440d38af427b4ac8eec7a 100644 (file)
@@ -62,6 +62,8 @@ CONFIG_TIPC=m
 CONFIG_LLC2=m
 CONFIG_DNS_RESOLVER=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 CONFIG_PARPORT=y
 CONFIG_PARPORT_PC=y
index dfe88f6c95c4d7c924b9e03d8c7abd236f100609..ba61495e1fa4b8d9ee576aff9a7559e619f229b6 100644 (file)
@@ -49,6 +49,8 @@ CONFIG_INET6_ESP=y
 CONFIG_INET6_IPCOMP=y
 CONFIG_LLC2=m
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_PARPORT=y
index b521c0adf4ec95317c2c00cd2c197bc8e3610c03..c035673209f732f0850aaa4dc98d2d49eee3b74c 100644 (file)
@@ -602,7 +602,6 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long
                __flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
        }
 }
-EXPORT_SYMBOL_GPL(flush_cache_page);
 
 #ifdef CONFIG_PARISC_TMPALIAS
 
index 37aabd772fbb91c3d4f07210a2543638cf4ef159..d2d58258aea68084c4f09cc6012a741b838b22d5 100644 (file)
@@ -195,6 +195,8 @@ common_stext:
        ldw             MEM_PDC_HI(%r0),%r6
        depd            %r6, 31, 32, %r3        /* move to upper word */
 
+       mfctl           %cr30,%r6               /* PCX-W2 firmware bug */
+
        ldo             PDC_PSW(%r0),%arg0              /* 21 */
        ldo             PDC_PSW_SET_DEFAULTS(%r0),%arg1 /* 2 */
        ldo             PDC_PSW_WIDE_BIT(%r0),%arg2     /* 2 */
@@ -203,6 +205,8 @@ common_stext:
        copy            %r0,%arg3
 
 stext_pdc_ret:
+       mtctl           %r6,%cr30               /* restore task thread info */
+
        /* restore rfi target address*/
        ldd             TI_TASK-THREAD_SZ_ALGN(%sp), %r10
        tophys_r1       %r10
index 16a7c2326d48d217b3f163777f3cfc473bb02fe1..1114d13ac19f6ec5e0294a2d197e2db3ec8e6814 100644 (file)
@@ -292,6 +292,7 @@ out:
                return rc;
        return count;
 }
+static BUS_ATTR(probe, S_IWUSR, NULL, ibmebus_store_probe);
 
 static ssize_t ibmebus_store_remove(struct bus_type *bus,
                                    const char *buf, size_t count)
@@ -317,13 +318,14 @@ static ssize_t ibmebus_store_remove(struct bus_type *bus,
                return -ENODEV;
        }
 }
+static BUS_ATTR(remove, S_IWUSR, NULL, ibmebus_store_remove);
 
-
-static struct bus_attribute ibmebus_bus_attrs[] = {
-       __ATTR(probe, S_IWUSR, NULL, ibmebus_store_probe),
-       __ATTR(remove, S_IWUSR, NULL, ibmebus_store_remove),
-       __ATTR_NULL
+static struct attribute *ibmbus_bus_attrs[] = {
+       &bus_attr_probe.attr,
+       &bus_attr_remove.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(ibmbus_bus);
 
 static int ibmebus_bus_bus_match(struct device *dev, struct device_driver *drv)
 {
@@ -713,7 +715,7 @@ static struct dev_pm_ops ibmebus_bus_dev_pm_ops = {
 struct bus_type ibmebus_bus_type = {
        .name      = "ibmebus",
        .uevent    = of_device_uevent_modalias,
-       .bus_attrs = ibmebus_bus_attrs,
+       .bus_groups = ibmbus_bus_groups,
        .match     = ibmebus_bus_bus_match,
        .probe     = ibmebus_bus_device_probe,
        .remove    = ibmebus_bus_device_remove,
index d38cc08b16c7f23831e3af558bfe35908e14af47..408956fbf4f617b67a284e9bd19f22c2fb86abd4 100644 (file)
@@ -997,21 +997,36 @@ static struct device_attribute vio_cmo_dev_attrs[] = {
 /* sysfs bus functions and data structures for CMO */
 
 #define viobus_cmo_rd_attr(name)                                        \
-static ssize_t                                                          \
-viobus_cmo_##name##_show(struct bus_type *bt, char *buf)                \
+static ssize_t cmo_##name##_show(struct bus_type *bt, char *buf)        \
 {                                                                       \
        return sprintf(buf, "%lu\n", vio_cmo.name);                     \
-}
+}                                                                       \
+static BUS_ATTR_RO(cmo_##name)
 
 #define viobus_cmo_pool_rd_attr(name, var)                              \
 static ssize_t                                                          \
-viobus_cmo_##name##_pool_show_##var(struct bus_type *bt, char *buf)     \
+cmo_##name##_##var##_show(struct bus_type *bt, char *buf)               \
 {                                                                       \
        return sprintf(buf, "%lu\n", vio_cmo.name.var);                 \
+}                                                                       \
+static BUS_ATTR_RO(cmo_##name##_##var)
+
+viobus_cmo_rd_attr(entitled);
+viobus_cmo_rd_attr(spare);
+viobus_cmo_rd_attr(min);
+viobus_cmo_rd_attr(desired);
+viobus_cmo_rd_attr(curr);
+viobus_cmo_pool_rd_attr(reserve, size);
+viobus_cmo_pool_rd_attr(excess, size);
+viobus_cmo_pool_rd_attr(excess, free);
+
+static ssize_t cmo_high_show(struct bus_type *bt, char *buf)
+{
+       return sprintf(buf, "%lu\n", vio_cmo.high);
 }
 
-static ssize_t viobus_cmo_high_reset(struct bus_type *bt, const char *buf,
-                                     size_t count)
+static ssize_t cmo_high_store(struct bus_type *bt, const char *buf,
+                             size_t count)
 {
        unsigned long flags;
 
@@ -1021,35 +1036,26 @@ static ssize_t viobus_cmo_high_reset(struct bus_type *bt, const char *buf,
 
        return count;
 }
-
-viobus_cmo_rd_attr(entitled);
-viobus_cmo_pool_rd_attr(reserve, size);
-viobus_cmo_pool_rd_attr(excess, size);
-viobus_cmo_pool_rd_attr(excess, free);
-viobus_cmo_rd_attr(spare);
-viobus_cmo_rd_attr(min);
-viobus_cmo_rd_attr(desired);
-viobus_cmo_rd_attr(curr);
-viobus_cmo_rd_attr(high);
-
-static struct bus_attribute vio_cmo_bus_attrs[] = {
-       __ATTR(cmo_entitled, S_IRUGO, viobus_cmo_entitled_show, NULL),
-       __ATTR(cmo_reserve_size, S_IRUGO, viobus_cmo_reserve_pool_show_size, NULL),
-       __ATTR(cmo_excess_size, S_IRUGO, viobus_cmo_excess_pool_show_size, NULL),
-       __ATTR(cmo_excess_free, S_IRUGO, viobus_cmo_excess_pool_show_free, NULL),
-       __ATTR(cmo_spare,   S_IRUGO, viobus_cmo_spare_show,   NULL),
-       __ATTR(cmo_min,     S_IRUGO, viobus_cmo_min_show,     NULL),
-       __ATTR(cmo_desired, S_IRUGO, viobus_cmo_desired_show, NULL),
-       __ATTR(cmo_curr,    S_IRUGO, viobus_cmo_curr_show,    NULL),
-       __ATTR(cmo_high,    S_IWUSR|S_IRUSR|S_IWGRP|S_IRGRP|S_IROTH,
-              viobus_cmo_high_show, viobus_cmo_high_reset),
-       __ATTR_NULL
+static BUS_ATTR_RW(cmo_high);
+
+static struct attribute *vio_bus_attrs[] = {
+       &bus_attr_cmo_entitled.attr,
+       &bus_attr_cmo_spare.attr,
+       &bus_attr_cmo_min.attr,
+       &bus_attr_cmo_desired.attr,
+       &bus_attr_cmo_curr.attr,
+       &bus_attr_cmo_high.attr,
+       &bus_attr_cmo_reserve_size.attr,
+       &bus_attr_cmo_excess_size.attr,
+       &bus_attr_cmo_excess_free.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(vio_bus);
 
 static void vio_cmo_sysfs_init(void)
 {
        vio_bus_type.dev_attrs = vio_cmo_dev_attrs;
-       vio_bus_type.bus_attrs = vio_cmo_bus_attrs;
+       vio_bus_type.bus_groups = vio_bus_groups;
 }
 #else /* CONFIG_PPC_SMLPAR */
 int vio_cmo_entitlement_update(size_t new_entitlement) { return 0; }
index bf56e33f8257f68717b241c98abefb2f925f2858..2345bdb4d91784bb2bbdd8ce05d9c3566e48f309 100644 (file)
@@ -691,4 +691,5 @@ void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter)
                module_free(NULL, fp->bpf_func);
+       kfree(fp);
 }
index 9b60a36c348d5422dc325463bcb26efeee64161d..2204400d0bd58d4a1e45c82394ff5cbd100aa2cc 100644 (file)
@@ -748,7 +748,9 @@ static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry)
 
 static inline void pgste_set_pte(pte_t *ptep, pte_t entry)
 {
-       if (!MACHINE_HAS_ESOP && (pte_val(entry) & _PAGE_WRITE)) {
+       if (!MACHINE_HAS_ESOP &&
+           (pte_val(entry) & _PAGE_PRESENT) &&
+           (pte_val(entry) & _PAGE_WRITE)) {
                /*
                 * Without enhanced suppression-on-protection force
                 * the dirty bit on for all writable ptes.
index 8ad8af915032a32573e06548e90d89ea7563349c..819b94d2272054d318fdd12c31b8718a9e4bb0bd 100644 (file)
@@ -71,30 +71,30 @@ static inline void local_tick_enable(unsigned long long comp)
 
 typedef unsigned long long cycles_t;
 
-static inline unsigned long long get_tod_clock(void)
-{
-       unsigned long long clk;
-
-#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES
-       asm volatile(".insn s,0xb27c0000,%0" : "=Q" (clk) : : "cc");
-#else
-       asm volatile("stck %0" : "=Q" (clk) : : "cc");
-#endif
-       return clk;
-}
-
 static inline void get_tod_clock_ext(char *clk)
 {
        asm volatile("stcke %0" : "=Q" (*clk) : : "cc");
 }
 
-static inline unsigned long long get_tod_clock_xt(void)
+static inline unsigned long long get_tod_clock(void)
 {
        unsigned char clk[16];
        get_tod_clock_ext(clk);
        return *((unsigned long long *)&clk[1]);
 }
 
+static inline unsigned long long get_tod_clock_fast(void)
+{
+#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES
+       unsigned long long clk;
+
+       asm volatile("stckf %0" : "=Q" (clk) : : "cc");
+       return clk;
+#else
+       return get_tod_clock();
+#endif
+}
+
 static inline cycles_t get_cycles(void)
 {
        return (cycles_t) get_tod_clock() >> 2;
@@ -125,7 +125,7 @@ extern u64 sched_clock_base_cc;
  */
 static inline unsigned long long get_tod_clock_monotonic(void)
 {
-       return get_tod_clock_xt() - sched_clock_base_cc;
+       return get_tod_clock() - sched_clock_base_cc;
 }
 
 /**
index 1389b637dae55018e3eab8b18dd3f44e9440e078..adaa9e9478d83fb77a2e91a41de0934f84acfb66 100644 (file)
@@ -99,7 +99,7 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
                        break;
                }
        }
-       return err;
+       return err ? -EFAULT : 0;
 }
 
 int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
@@ -148,7 +148,7 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
                        break;
                }
        }
-       return err;
+       return err ? -EFAULT : 0;
 }
 
 static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)
index f1279dc2e1bcecae2a12ea246096f1458b2de77f..17d62fe5d7b70554c36b8f85b34da23d86c90407 100644 (file)
@@ -867,7 +867,7 @@ static inline void
 debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level,
                        int exception)
 {
-       active->id.stck = get_tod_clock();
+       active->id.stck = get_tod_clock_fast();
        active->id.fields.cpuid = smp_processor_id();
        active->caller = __builtin_return_address(0);
        active->id.fields.exception = exception;
index 7f35cb33e5102008244b4fd1376954400f2d0009..7f1f7ac5cf7f8a2c3f3966d4fe96fa23af90ea04 100644 (file)
@@ -385,7 +385,7 @@ static int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
        }
 
        if ((!rc) && (vcpu->arch.sie_block->ckc <
-               get_tod_clock() + vcpu->arch.sie_block->epoch)) {
+               get_tod_clock_fast() + vcpu->arch.sie_block->epoch)) {
                if ((!psw_extint_disabled(vcpu)) &&
                        (vcpu->arch.sie_block->gcr[0] & 0x800ul))
                        rc = 1;
@@ -425,7 +425,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
                goto no_timer;
        }
 
-       now = get_tod_clock() + vcpu->arch.sie_block->epoch;
+       now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch;
        if (vcpu->arch.sie_block->ckc < now) {
                __unset_cpu_idle(vcpu);
                return 0;
@@ -515,7 +515,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
        }
 
        if ((vcpu->arch.sie_block->ckc <
-               get_tod_clock() + vcpu->arch.sie_block->epoch))
+               get_tod_clock_fast() + vcpu->arch.sie_block->epoch))
                __try_deliver_ckc_interrupt(vcpu);
 
        if (atomic_read(&fi->active)) {
index 57c87d7d7ede01add784c291f31a82f502ca877e..a9f3d0042d58ba849b8c624f23915d88d03d6f70 100644 (file)
@@ -44,7 +44,7 @@ static void __udelay_disabled(unsigned long long usecs)
        do {
                set_clock_comparator(end);
                vtime_stop_cpu();
-       } while (get_tod_clock() < end);
+       } while (get_tod_clock_fast() < end);
        lockdep_on();
        __ctl_load(cr0, 0, 0);
        __ctl_load(cr6, 6, 6);
@@ -55,7 +55,7 @@ static void __udelay_enabled(unsigned long long usecs)
 {
        u64 clock_saved, end;
 
-       end = get_tod_clock() + (usecs << 12);
+       end = get_tod_clock_fast() + (usecs << 12);
        do {
                clock_saved = 0;
                if (end < S390_lowcore.clock_comparator) {
@@ -65,7 +65,7 @@ static void __udelay_enabled(unsigned long long usecs)
                vtime_stop_cpu();
                if (clock_saved)
                        local_tick_enable(clock_saved);
-       } while (get_tod_clock() < end);
+       } while (get_tod_clock_fast() < end);
 }
 
 /*
@@ -109,8 +109,8 @@ void udelay_simple(unsigned long long usecs)
 {
        u64 end;
 
-       end = get_tod_clock() + (usecs << 12);
-       while (get_tod_clock() < end)
+       end = get_tod_clock_fast() + (usecs << 12);
+       while (get_tod_clock_fast() < end)
                cpu_relax();
 }
 
@@ -120,10 +120,10 @@ void __ndelay(unsigned long long nsecs)
 
        nsecs <<= 9;
        do_div(nsecs, 125);
-       end = get_tod_clock() + nsecs;
+       end = get_tod_clock_fast() + nsecs;
        if (nsecs & ~0xfffUL)
                __udelay(nsecs >> 12);
-       while (get_tod_clock() < end)
+       while (get_tod_clock_fast() < end)
                barrier();
 }
 EXPORT_SYMBOL(__ndelay);
index 709239285869caa29fde3db90ea31da213cb5cb2..a5df511e27a220cf102d8ffee6b7b57f903eb8c3 100644 (file)
@@ -881,7 +881,9 @@ void bpf_jit_free(struct sk_filter *fp)
        struct bpf_binary_header *header = (void *)addr;
 
        if (fp->bpf_func == sk_run_filter)
-               return;
+               goto free_filter;
        set_memory_rw(addr, header->pages);
        module_free(NULL, header);
+free_filter:
+       kfree(fp);
 }
index 9c7be59e6f5ad3ad360facd10c6a2c35fd3bde75..218b6b23c378f888ef6ab0accf7ee4882e2911e2 100644 (file)
@@ -808,4 +808,5 @@ void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter)
                module_free(NULL, fp->bpf_func);
+       kfree(fp);
 }
index 829df49dee99c6093d5586d0e2c19e34786dfa97..41ebbfebb3332f2bc7269a754ff96b377e17212b 100644 (file)
@@ -40,9 +40,11 @@ static ssize_t exitcode_proc_write(struct file *file,
                const char __user *buffer, size_t count, loff_t *pos)
 {
        char *end, buf[sizeof("nnnnn\0")];
+       size_t size;
        int tmp;
 
-       if (copy_from_user(buf, buffer, count))
+       size = min(count, sizeof(buf));
+       if (copy_from_user(buf, buffer, size))
                return -EFAULT;
 
        tmp = simple_strtol(buf, &end, 0);
index 0da5200ee79d149bdefe5aaddf917a536c85caa4..b3e18f800302ffee529e9aa99a22623118600042 100644 (file)
@@ -128,7 +128,8 @@ do {                                                        \
 do {                                                                   \
        typedef typeof(var) pao_T__;                                    \
        const int pao_ID__ = (__builtin_constant_p(val) &&              \
-                             ((val) == 1 || (val) == -1)) ? (val) : 0; \
+                             ((val) == 1 || (val) == -1)) ?            \
+                               (int)(val) : 0;                         \
        if (0) {                                                        \
                pao_T__ pao_tmp__;                                      \
                pao_tmp__ = (val);                                      \
index 9d8449158cf989af3009c6606b7a878c827f5d32..8a87a3224121fdeac2d2fd59fb2142f6de8d8a24 100644 (file)
@@ -1276,16 +1276,16 @@ void perf_events_lapic_init(void)
 static int __kprobes
 perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 {
-       int ret;
        u64 start_clock;
        u64 finish_clock;
+       int ret;
 
        if (!atomic_read(&active_events))
                return NMI_DONE;
 
-       start_clock = local_clock();
+       start_clock = sched_clock();
        ret = x86_pmu.handle_irq(regs);
-       finish_clock = local_clock();
+       finish_clock = sched_clock();
 
        perf_sample_event_took(finish_clock - start_clock);
 
index a0e2a8a80c94129bb3d3d47bf975ef68f98f500e..b2046e4d0b59e9b2b50720783b2aec4145012985 100644 (file)
@@ -609,7 +609,7 @@ static struct dentry *d_kvm_debug;
 
 struct dentry *kvm_init_debugfs(void)
 {
-       d_kvm_debug = debugfs_create_dir("kvm", NULL);
+       d_kvm_debug = debugfs_create_dir("kvm-guest", NULL);
        if (!d_kvm_debug)
                printk(KERN_WARNING "Could not create 'kvm' debugfs directory\n");
 
index ba77ebc2c35321dc4085c06a106f715ec18d9a21..6fcb49ce50a1260d1f4bfdf0b5a065dd4f6d77c7 100644 (file)
@@ -113,10 +113,10 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2
                u64 before, delta, whole_msecs;
                int remainder_ns, decimal_msecs, thishandled;
 
-               before = local_clock();
+               before = sched_clock();
                thishandled = a->handler(type, regs);
                handled += thishandled;
-               delta = local_clock() - before;
+               delta = sched_clock() - before;
                trace_nmi_handler(a->handler, (int)delta, thishandled);
 
                if (delta < nmi_longest_ns)
index 79c216aa0e2baaac3a65a43972161922489c4978..516593e1ce33b92175195c8de2983258b723493c 100644 (file)
@@ -772,13 +772,21 @@ out:
        return;
 }
 
+static void bpf_jit_free_deferred(struct work_struct *work)
+{
+       struct sk_filter *fp = container_of(work, struct sk_filter, work);
+       unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
+       struct bpf_binary_header *header = (void *)addr;
+
+       set_memory_rw(addr, header->pages);
+       module_free(NULL, header);
+       kfree(fp);
+}
+
 void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter) {
-               unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
-               struct bpf_binary_header *header = (void *)addr;
-
-               set_memory_rw(addr, header->pages);
-               module_free(NULL, header);
+               INIT_WORK(&fp->work, bpf_jit_free_deferred);
+               schedule_work(&fp->work);
        }
 }
index de1dfa18d0a11953c037af6c276603c20b1e346f..21dbe6bdb8edc661d729f656a02a5f1cf01a18c1 100644 (file)
@@ -1122,7 +1122,7 @@ ENDPROC(fast_syscall_spill_registers)
  * a3: exctable, original value in excsave1
  */
 
-fast_syscall_spill_registers_fixup:
+ENTRY(fast_syscall_spill_registers_fixup)
 
        rsr     a2, windowbase  # get current windowbase (a2 is saved)
        xsr     a0, depc        # restore depc and a0
@@ -1134,22 +1134,26 @@ fast_syscall_spill_registers_fixup:
         */
 
        xsr     a3, excsave1    # get spill-mask
-       slli    a2, a3, 1       # shift left by one
+       slli    a3, a3, 1       # shift left by one
 
-       slli    a3, a2, 32-WSBITS
-       src     a2, a2, a3      # a1 = xxwww1yyxxxwww1yy......
+       slli    a2, a3, 32-WSBITS
+       src     a2, a3, a2      # a2 = xxwww1yyxxxwww1yy......
        wsr     a2, windowstart # set corrected windowstart
 
-       rsr     a3, excsave1
-       l32i    a2, a3, EXC_TABLE_DOUBLE_SAVE   # restore a2
-       l32i    a3, a3, EXC_TABLE_PARAM # original WB (in user task)
+       srli    a3, a3, 1
+       rsr     a2, excsave1
+       l32i    a2, a2, EXC_TABLE_DOUBLE_SAVE   # restore a2
+       xsr     a2, excsave1
+       s32i    a3, a2, EXC_TABLE_DOUBLE_SAVE   # save a3
+       l32i    a3, a2, EXC_TABLE_PARAM # original WB (in user task)
+       xsr     a2, excsave1
 
        /* Return to the original (user task) WINDOWBASE.
         * We leave the following frame behind:
         * a0, a1, a2   same
-        * a3:          trashed (saved in excsave_1)
+        * a3:          trashed (saved in EXC_TABLE_DOUBLE_SAVE)
         * depc:        depc (we have to return to that address)
-        * excsave_1:   a3
+        * excsave_1:   exctable
         */
 
        wsr     a3, windowbase
@@ -1159,9 +1163,9 @@ fast_syscall_spill_registers_fixup:
         *  a0: return address
         *  a1: used, stack pointer
         *  a2: kernel stack pointer
-        *  a3: available, saved in EXCSAVE_1
+        *  a3: available
         *  depc: exception address
-        *  excsave: a3
+        *  excsave: exctable
         * Note: This frame might be the same as above.
         */
 
@@ -1181,9 +1185,12 @@ fast_syscall_spill_registers_fixup:
        rsr     a0, exccause
        addx4   a0, a0, a3                      # find entry in table
        l32i    a0, a0, EXC_TABLE_FAST_USER     # load handler
+       l32i    a3, a3, EXC_TABLE_DOUBLE_SAVE
        jx      a0
 
-fast_syscall_spill_registers_fixup_return:
+ENDPROC(fast_syscall_spill_registers_fixup)
+
+ENTRY(fast_syscall_spill_registers_fixup_return)
 
        /* When we return here, all registers have been restored (a2: DEPC) */
 
@@ -1191,13 +1198,13 @@ fast_syscall_spill_registers_fixup_return:
 
        /* Restore fixup handler. */
 
-       xsr     a3, excsave1
-       movi    a2, fast_syscall_spill_registers_fixup
-       s32i    a2, a3, EXC_TABLE_FIXUP
-       s32i    a0, a3, EXC_TABLE_DOUBLE_SAVE
-       rsr     a2, windowbase
-       s32i    a2, a3, EXC_TABLE_PARAM
-       l32i    a2, a3, EXC_TABLE_KSTK
+       rsr     a2, excsave1
+       s32i    a3, a2, EXC_TABLE_DOUBLE_SAVE
+       movi    a3, fast_syscall_spill_registers_fixup
+       s32i    a3, a2, EXC_TABLE_FIXUP
+       rsr     a3, windowbase
+       s32i    a3, a2, EXC_TABLE_PARAM
+       l32i    a2, a2, EXC_TABLE_KSTK
 
        /* Load WB at the time the exception occurred. */
 
@@ -1206,8 +1213,12 @@ fast_syscall_spill_registers_fixup_return:
        wsr     a3, windowbase
        rsync
 
+       rsr     a3, excsave1
+       l32i    a3, a3, EXC_TABLE_DOUBLE_SAVE
+
        rfde
 
+ENDPROC(fast_syscall_spill_registers_fixup_return)
 
 /*
  * spill all registers.
index 718eca1850bd3533762aa64914d1a33e544d43ce..98b67d5f15144659dd9ea6954ab440e964a2aae8 100644 (file)
@@ -341,7 +341,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
 
        sp = regs->areg[1];
 
-       if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) {
+       if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && sas_ss_flags(sp) == 0) {
                sp = current->sas_ss_sp + current->sas_ss_size;
        }
 
index 56f88b7afe2fd907798cd41023133e11c37cf7c8..e9e1aad8c271048d4fde5bdac7ebef7e1cc79a96 100644 (file)
@@ -737,7 +737,8 @@ static int __init iss_net_setup(char *str)
                return 1;
        }
 
-       if ((new = alloc_bootmem(sizeof new)) == NULL) {
+       new = alloc_bootmem(sizeof(*new));
+       if (new == NULL) {
                printk("Alloc_bootmem failed\n");
                return 1;
        }
index aa43b911ccef582c78e70a914106ba454de2c05b..8f451449abd3dc05651414483bceba221d6136f0 100644 (file)
@@ -166,4 +166,6 @@ source "drivers/reset/Kconfig"
 
 source "drivers/fmc/Kconfig"
 
+source "drivers/phy/Kconfig"
+
 endmenu
index ab93de8297f1338fc2440967ce8934a7ff6bc043..687da899cadb5b0bbe1dd7e04ec6b56d1d000022 100644 (file)
@@ -8,6 +8,8 @@
 obj-y                          += irqchip/
 obj-y                          += bus/
 
+obj-$(CONFIG_GENERIC_PHY)      += phy/
+
 # GPIO must come after pinctrl as gpios may need to mux pins etc
 obj-y                          += pinctrl/
 obj-y                          += gpio/
index 9d715ae5ff6b73b6bd09690b95e9686f196e9cdb..8e28f923cf7f3a221104eb625e4d3a89b5ab79ca 100644 (file)
@@ -1343,7 +1343,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
                host->flags |= ATA_HOST_PARALLEL_SCAN;
        else
-               printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n");
+               dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n");
 
        if (pi.flags & ATA_FLAG_EM)
                ahci_reset_em(host);
index 2daaee05cab12d629502c913ef7102a793cdf27f..7d3b85385bfc16b92ffe303acf38f343331dfc2e 100644 (file)
@@ -184,7 +184,7 @@ static int ahci_probe(struct platform_device *pdev)
        if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
                host->flags |= ATA_HOST_PARALLEL_SCAN;
        else
-               printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n");
+               dev_info(dev, "SSS flag set, parallel bus scan disabled\n");
 
        if (pi.flags & ATA_FLAG_EM)
                ahci_reset_em(host);
index acfd0f711069e9da304c95179b14127e20ae1c9c..aaac4fb0d5645f8d52782fc55cb6f39da04830e7 100644 (file)
@@ -778,8 +778,16 @@ static void ahci_start_port(struct ata_port *ap)
                                rc = ap->ops->transmit_led_message(ap,
                                                               emp->led_state,
                                                               4);
+                               /*
+                                * If busy, give a breather but do not
+                                * release EH ownership by using msleep()
+                                * instead of ata_msleep().  EM Transmit
+                                * bit is busy for the whole host and
+                                * releasing ownership will cause other
+                                * ports to fail the same way.
+                                */
                                if (rc == -EBUSY)
-                                       ata_msleep(ap, 1);
+                                       msleep(1);
                                else
                                        break;
                        }
index c69fcce505c03d06c7b20ae333ff9708a228f869..370462fa8e01addd3387befee8f4ea78486b2cc0 100644 (file)
@@ -1322,14 +1322,14 @@ void ata_eh_qc_complete(struct ata_queued_cmd *qc)
  *     should be retried.  To be used from EH.
  *
  *     SCSI midlayer limits the number of retries to scmd->allowed.
- *     scmd->retries is decremented for commands which get retried
+ *     scmd->allowed is incremented for commands which get retried
  *     due to unrelated failures (qc->err_mask is zero).
  */
 void ata_eh_qc_retry(struct ata_queued_cmd *qc)
 {
        struct scsi_cmnd *scmd = qc->scsicmd;
-       if (!qc->err_mask && scmd->retries)
-               scmd->retries--;
+       if (!qc->err_mask)
+               scmd->allowed++;
        __ata_eh_qc_complete(qc);
 }
 
index 4bceb8803a10f50baee31c9c39233282ebbc6e91..b33d1f99b3a44bb40e76a529a8eddbc2268bdc20 100644 (file)
@@ -78,7 +78,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev
 
        ap->ioaddr.cmd_addr = cmd_addr;
 
-       if (pnp_port_valid(idev, 1) == 0) {
+       if (pnp_port_valid(idev, 1)) {
                ctl_addr = devm_ioport_map(&idev->dev,
                                           pnp_port_start(idev, 1), 1);
                ap->ioaddr.altstatus_addr = ctl_addr;
index 4c289ab91357531373c25889b5911bff33ae3d64..73f6c2925281f43ab752e0a374c059133e2f61ef 100644 (file)
@@ -591,37 +591,6 @@ void bus_remove_device(struct device *dev)
        bus_put(dev->bus);
 }
 
-static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv)
-{
-       int error = 0;
-       int i;
-
-       if (bus->drv_attrs) {
-               for (i = 0; bus->drv_attrs[i].attr.name; i++) {
-                       error = driver_create_file(drv, &bus->drv_attrs[i]);
-                       if (error)
-                               goto err;
-               }
-       }
-done:
-       return error;
-err:
-       while (--i >= 0)
-               driver_remove_file(drv, &bus->drv_attrs[i]);
-       goto done;
-}
-
-static void driver_remove_attrs(struct bus_type *bus,
-                               struct device_driver *drv)
-{
-       int i;
-
-       if (bus->drv_attrs) {
-               for (i = 0; bus->drv_attrs[i].attr.name; i++)
-                       driver_remove_file(drv, &bus->drv_attrs[i]);
-       }
-}
-
 static int __must_check add_bind_files(struct device_driver *drv)
 {
        int ret;
@@ -720,16 +689,12 @@ int bus_add_driver(struct device_driver *drv)
                printk(KERN_ERR "%s: uevent attr (%s) failed\n",
                        __func__, drv->name);
        }
-       error = driver_add_attrs(bus, drv);
+       error = driver_add_groups(drv, bus->drv_groups);
        if (error) {
                /* How the hell do we get out of this pickle? Give up */
-               printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
-                       __func__, drv->name);
-       }
-       error = driver_add_groups(drv, bus->drv_groups);
-       if (error)
                printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
                        __func__, drv->name);
+       }
 
        if (!drv->suppress_bind_attrs) {
                error = add_bind_files(drv);
@@ -766,7 +731,6 @@ void bus_remove_driver(struct device_driver *drv)
 
        if (!drv->suppress_bind_attrs)
                remove_bind_files(drv);
-       driver_remove_attrs(drv->bus, drv);
        driver_remove_groups(drv, drv->bus->drv_groups);
        driver_remove_file(drv, &driver_attr_uevent);
        klist_remove(&drv->p->knode_bus);
@@ -846,42 +810,6 @@ struct bus_type *find_bus(char *name)
 }
 #endif  /*  0  */
 
-
-/**
- * bus_add_attrs - Add default attributes for this bus.
- * @bus: Bus that has just been registered.
- */
-
-static int bus_add_attrs(struct bus_type *bus)
-{
-       int error = 0;
-       int i;
-
-       if (bus->bus_attrs) {
-               for (i = 0; bus->bus_attrs[i].attr.name; i++) {
-                       error = bus_create_file(bus, &bus->bus_attrs[i]);
-                       if (error)
-                               goto err;
-               }
-       }
-done:
-       return error;
-err:
-       while (--i >= 0)
-               bus_remove_file(bus, &bus->bus_attrs[i]);
-       goto done;
-}
-
-static void bus_remove_attrs(struct bus_type *bus)
-{
-       int i;
-
-       if (bus->bus_attrs) {
-               for (i = 0; bus->bus_attrs[i].attr.name; i++)
-                       bus_remove_file(bus, &bus->bus_attrs[i]);
-       }
-}
-
 static int bus_add_groups(struct bus_type *bus,
                          const struct attribute_group **groups)
 {
@@ -983,9 +911,6 @@ int bus_register(struct bus_type *bus)
        if (retval)
                goto bus_probe_files_fail;
 
-       retval = bus_add_attrs(bus);
-       if (retval)
-               goto bus_attrs_fail;
        retval = bus_add_groups(bus, bus->bus_groups);
        if (retval)
                goto bus_groups_fail;
@@ -994,8 +919,6 @@ int bus_register(struct bus_type *bus)
        return 0;
 
 bus_groups_fail:
-       bus_remove_attrs(bus);
-bus_attrs_fail:
        remove_probe_files(bus);
 bus_probe_files_fail:
        kset_unregister(bus->p->drivers_kset);
@@ -1024,7 +947,6 @@ void bus_unregister(struct bus_type *bus)
        pr_debug("bus: '%s': unregistering\n", bus->name);
        if (bus->dev_root)
                device_unregister(bus->dev_root);
-       bus_remove_attrs(bus);
        bus_remove_groups(bus, bus->bus_groups);
        remove_probe_files(bus);
        kset_unregister(bus->p->drivers_kset);
index 8b7818b800569b287536cb366080af3e92f393f2..f96f70419a785a722283336a4ca71a73015574b9 100644 (file)
@@ -47,18 +47,6 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,
        return ret;
 }
 
-static const void *class_attr_namespace(struct kobject *kobj,
-                                       const struct attribute *attr)
-{
-       struct class_attribute *class_attr = to_class_attr(attr);
-       struct subsys_private *cp = to_subsys_private(kobj);
-       const void *ns = NULL;
-
-       if (class_attr->namespace)
-               ns = class_attr->namespace(cp->class, class_attr);
-       return ns;
-}
-
 static void class_release(struct kobject *kobj)
 {
        struct subsys_private *cp = to_subsys_private(kobj);
@@ -86,7 +74,6 @@ static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject
 static const struct sysfs_ops class_sysfs_ops = {
        .show      = class_attr_show,
        .store     = class_attr_store,
-       .namespace = class_attr_namespace,
 };
 
 static struct kobj_type class_ktype = {
@@ -99,21 +86,23 @@ static struct kobj_type class_ktype = {
 static struct kset *class_kset;
 
 
-int class_create_file(struct class *cls, const struct class_attribute *attr)
+int class_create_file_ns(struct class *cls, const struct class_attribute *attr,
+                        const void *ns)
 {
        int error;
        if (cls)
-               error = sysfs_create_file(&cls->p->subsys.kobj,
-                                         &attr->attr);
+               error = sysfs_create_file_ns(&cls->p->subsys.kobj,
+                                            &attr->attr, ns);
        else
                error = -EINVAL;
        return error;
 }
 
-void class_remove_file(struct class *cls, const struct class_attribute *attr)
+void class_remove_file_ns(struct class *cls, const struct class_attribute *attr,
+                         const void *ns)
 {
        if (cls)
-               sysfs_remove_file(&cls->p->subsys.kobj, &attr->attr);
+               sysfs_remove_file_ns(&cls->p->subsys.kobj, &attr->attr, ns);
 }
 
 static struct class *class_get(struct class *cls)
@@ -600,8 +589,8 @@ int __init classes_init(void)
        return 0;
 }
 
-EXPORT_SYMBOL_GPL(class_create_file);
-EXPORT_SYMBOL_GPL(class_remove_file);
+EXPORT_SYMBOL_GPL(class_create_file_ns);
+EXPORT_SYMBOL_GPL(class_remove_file_ns);
 EXPORT_SYMBOL_GPL(class_unregister);
 EXPORT_SYMBOL_GPL(class_destroy);
 
index 34abf4d8a45ff4f3f6d25787887a8bc787779133..67b180d855b2c7baf365e5174802581b3c766745 100644 (file)
@@ -455,64 +455,6 @@ static ssize_t online_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(online);
 
-static int device_add_attributes(struct device *dev,
-                                struct device_attribute *attrs)
-{
-       int error = 0;
-       int i;
-
-       if (attrs) {
-               for (i = 0; attrs[i].attr.name; i++) {
-                       error = device_create_file(dev, &attrs[i]);
-                       if (error)
-                               break;
-               }
-               if (error)
-                       while (--i >= 0)
-                               device_remove_file(dev, &attrs[i]);
-       }
-       return error;
-}
-
-static void device_remove_attributes(struct device *dev,
-                                    struct device_attribute *attrs)
-{
-       int i;
-
-       if (attrs)
-               for (i = 0; attrs[i].attr.name; i++)
-                       device_remove_file(dev, &attrs[i]);
-}
-
-static int device_add_bin_attributes(struct device *dev,
-                                    struct bin_attribute *attrs)
-{
-       int error = 0;
-       int i;
-
-       if (attrs) {
-               for (i = 0; attrs[i].attr.name; i++) {
-                       error = device_create_bin_file(dev, &attrs[i]);
-                       if (error)
-                               break;
-               }
-               if (error)
-                       while (--i >= 0)
-                               device_remove_bin_file(dev, &attrs[i]);
-       }
-       return error;
-}
-
-static void device_remove_bin_attributes(struct device *dev,
-                                        struct bin_attribute *attrs)
-{
-       int i;
-
-       if (attrs)
-               for (i = 0; attrs[i].attr.name; i++)
-                       device_remove_bin_file(dev, &attrs[i]);
-}
-
 int device_add_groups(struct device *dev, const struct attribute_group **groups)
 {
        return sysfs_create_groups(&dev->kobj, groups);
@@ -534,18 +476,12 @@ static int device_add_attrs(struct device *dev)
                error = device_add_groups(dev, class->dev_groups);
                if (error)
                        return error;
-               error = device_add_attributes(dev, class->dev_attrs);
-               if (error)
-                       goto err_remove_class_groups;
-               error = device_add_bin_attributes(dev, class->dev_bin_attrs);
-               if (error)
-                       goto err_remove_class_attrs;
        }
 
        if (type) {
                error = device_add_groups(dev, type->groups);
                if (error)
-                       goto err_remove_class_bin_attrs;
+                       goto err_remove_class_groups;
        }
 
        error = device_add_groups(dev, dev->groups);
@@ -563,12 +499,6 @@ static int device_add_attrs(struct device *dev)
  err_remove_type_groups:
        if (type)
                device_remove_groups(dev, type->groups);
- err_remove_class_bin_attrs:
-       if (class)
-               device_remove_bin_attributes(dev, class->dev_bin_attrs);
- err_remove_class_attrs:
-       if (class)
-               device_remove_attributes(dev, class->dev_attrs);
  err_remove_class_groups:
        if (class)
                device_remove_groups(dev, class->dev_groups);
@@ -587,11 +517,8 @@ static void device_remove_attrs(struct device *dev)
        if (type)
                device_remove_groups(dev, type->groups);
 
-       if (class) {
-               device_remove_attributes(dev, class->dev_attrs);
-               device_remove_bin_attributes(dev, class->dev_bin_attrs);
+       if (class)
                device_remove_groups(dev, class->dev_groups);
-       }
 }
 
 static ssize_t dev_show(struct device *dev, struct device_attribute *attr,
@@ -1881,6 +1808,7 @@ EXPORT_SYMBOL_GPL(device_destroy);
  */
 int device_rename(struct device *dev, const char *new_name)
 {
+       struct kobject *kobj = &dev->kobj;
        char *old_device_name = NULL;
        int error;
 
@@ -1888,8 +1816,7 @@ int device_rename(struct device *dev, const char *new_name)
        if (!dev)
                return -EINVAL;
 
-       pr_debug("device: '%s': %s: renaming to '%s'\n", dev_name(dev),
-                __func__, new_name);
+       dev_dbg(dev, "renaming to %s\n", new_name);
 
        old_device_name = kstrdup(dev_name(dev), GFP_KERNEL);
        if (!old_device_name) {
@@ -1898,13 +1825,14 @@ int device_rename(struct device *dev, const char *new_name)
        }
 
        if (dev->class) {
-               error = sysfs_rename_link(&dev->class->p->subsys.kobj,
-                       &dev->kobj, old_device_name, new_name);
+               error = sysfs_rename_link_ns(&dev->class->p->subsys.kobj,
+                                            kobj, old_device_name,
+                                            new_name, kobject_namespace(kobj));
                if (error)
                        goto out;
        }
 
-       error = kobject_rename(&dev->kobj, new_name);
+       error = kobject_rename(kobj, new_name);
        if (error)
                goto out;
 
index 507379e7b763781816ae5e4c65858b3b0b977a7b..545c4de412c3e0ba5a2680d9143e059a9c475dbd 100644 (file)
@@ -91,7 +91,8 @@ static __always_inline struct devres * alloc_dr(dr_release_t release,
        if (unlikely(!dr))
                return NULL;
 
-       memset(dr, 0, tot_size);
+       memset(dr, 0, offsetof(struct devres, data));
+
        INIT_LIST_HEAD(&dr->node.entry);
        dr->node.release = release;
        return dr;
@@ -110,7 +111,7 @@ void * __devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
 {
        struct devres *dr;
 
-       dr = alloc_dr(release, size, gfp);
+       dr = alloc_dr(release, size, gfp | __GFP_ZERO);
        if (unlikely(!dr))
                return NULL;
        set_node_dbginfo(&dr->node, name, size);
@@ -135,7 +136,7 @@ void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
 {
        struct devres *dr;
 
-       dr = alloc_dr(release, size, gfp);
+       dr = alloc_dr(release, size, gfp | __GFP_ZERO);
        if (unlikely(!dr))
                return NULL;
        return dr->data;
@@ -745,58 +746,62 @@ void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
 EXPORT_SYMBOL_GPL(devm_remove_action);
 
 /*
- * Managed kzalloc/kfree
+ * Managed kmalloc/kfree
  */
-static void devm_kzalloc_release(struct device *dev, void *res)
+static void devm_kmalloc_release(struct device *dev, void *res)
 {
        /* noop */
 }
 
-static int devm_kzalloc_match(struct device *dev, void *res, void *data)
+static int devm_kmalloc_match(struct device *dev, void *res, void *data)
 {
        return res == data;
 }
 
 /**
- * devm_kzalloc - Resource-managed kzalloc
+ * devm_kmalloc - Resource-managed kmalloc
  * @dev: Device to allocate memory for
  * @size: Allocation size
  * @gfp: Allocation gfp flags
  *
- * Managed kzalloc.  Memory allocated with this function is
+ * Managed kmalloc.  Memory allocated with this function is
  * automatically freed on driver detach.  Like all other devres
  * resources, guaranteed alignment is unsigned long long.
  *
  * RETURNS:
  * Pointer to allocated memory on success, NULL on failure.
  */
-void * devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
+void * devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
 {
        struct devres *dr;
 
        /* use raw alloc_dr for kmalloc caller tracing */
-       dr = alloc_dr(devm_kzalloc_release, size, gfp);
+       dr = alloc_dr(devm_kmalloc_release, size, gfp);
        if (unlikely(!dr))
                return NULL;
 
+       /*
+        * This is named devm_kzalloc_release for historical reasons
+        * The initial implementation did not support kmalloc, only kzalloc
+        */
        set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
        devres_add(dev, dr->data);
        return dr->data;
 }
-EXPORT_SYMBOL_GPL(devm_kzalloc);
+EXPORT_SYMBOL_GPL(devm_kmalloc);
 
 /**
  * devm_kfree - Resource-managed kfree
  * @dev: Device this memory belongs to
  * @p: Memory to free
  *
- * Free memory allocated with devm_kzalloc().
+ * Free memory allocated with devm_kmalloc().
  */
 void devm_kfree(struct device *dev, void *p)
 {
        int rc;
 
-       rc = devres_destroy(dev, devm_kzalloc_release, devm_kzalloc_match, p);
+       rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
        WARN_ON(rc);
 }
 EXPORT_SYMBOL_GPL(devm_kfree);
index 10a4467c63f14b424ea69a8a8c9af3f57c4a85f8..eb8fb94ae2c527b713c21092fcd3b7b0d42e25a0 100644 (file)
@@ -282,31 +282,35 @@ static noinline_for_stack long fw_file_size(struct file *file)
        return st.size;
 }
 
-static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf)
+static int fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf)
 {
        long size;
        char *buf;
+       int rc;
 
        size = fw_file_size(file);
        if (size <= 0)
-               return false;
+               return -EINVAL;
        buf = vmalloc(size);
        if (!buf)
-               return false;
-       if (kernel_read(file, 0, buf, size) != size) {
+               return -ENOMEM;
+       rc = kernel_read(file, 0, buf, size);
+       if (rc != size) {
+               if (rc > 0)
+                       rc = -EIO;
                vfree(buf);
-               return false;
+               return rc;
        }
        fw_buf->data = buf;
        fw_buf->size = size;
-       return true;
+       return 0;
 }
 
-static bool fw_get_filesystem_firmware(struct device *device,
+static int fw_get_filesystem_firmware(struct device *device,
                                       struct firmware_buf *buf)
 {
        int i;
-       bool success = false;
+       int rc = -ENOENT;
        char *path = __getname();
 
        for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
@@ -321,14 +325,17 @@ static bool fw_get_filesystem_firmware(struct device *device,
                file = filp_open(path, O_RDONLY, 0);
                if (IS_ERR(file))
                        continue;
-               success = fw_read_file_contents(file, buf);
+               rc = fw_read_file_contents(file, buf);
                fput(file);
-               if (success)
+               if (rc)
+                       dev_warn(device, "firmware, attempted to load %s, but failed with error %d\n",
+                               path, rc);
+               else
                        break;
        }
        __putname(path);
 
-       if (success) {
+       if (!rc) {
                dev_dbg(device, "firmware: direct-loading firmware %s\n",
                        buf->fw_id);
                mutex_lock(&fw_lock);
@@ -337,7 +344,7 @@ static bool fw_get_filesystem_firmware(struct device *device,
                mutex_unlock(&fw_lock);
        }
 
-       return success;
+       return rc;
 }
 
 /* firmware holds the ownership of pages */
@@ -1086,9 +1093,14 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
                }
        }
 
-       if (!fw_get_filesystem_firmware(device, fw->priv))
+       ret = fw_get_filesystem_firmware(device, fw->priv);
+       if (ret) {
+               dev_warn(device, "Direct firmware load failed with error %d\n",
+                        ret);
+               dev_warn(device, "Falling back to user helper\n");
                ret = fw_load_from_user_helper(fw, name, device,
                                               uevent, nowait, timeout);
+       }
 
        /* don't cache firmware handled without uevent */
        if (!ret)
index 4f8bef3eb5a8f77b9727b963ea4a5107b8e723e3..47051cd251132ce97752bc8f8f9748be272c0f35 100644 (file)
@@ -488,6 +488,11 @@ static int platform_drv_probe(struct device *_dev)
        if (ret && ACPI_HANDLE(_dev))
                acpi_dev_pm_detach(_dev, true);
 
+       if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
+               dev_warn(_dev, "probe deferral not supported\n");
+               ret = -ENXIO;
+       }
+
        return ret;
 }
 
@@ -553,8 +558,7 @@ EXPORT_SYMBOL_GPL(platform_driver_unregister);
 /**
  * platform_driver_probe - register driver for non-hotpluggable device
  * @drv: platform driver structure
- * @probe: the driver probe routine, probably from an __init section,
- *         must not return -EPROBE_DEFER.
+ * @probe: the driver probe routine, probably from an __init section
  *
  * Use this instead of platform_driver_register() when you know the device
  * is not hotpluggable and has already been registered, and you want to
@@ -565,8 +569,7 @@ EXPORT_SYMBOL_GPL(platform_driver_unregister);
  * into system-on-chip processors, where the controller devices have been
  * configured as part of board setup.
  *
- * This is incompatible with deferred probing so probe() must not
- * return -EPROBE_DEFER.
+ * Note that this is incompatible with deferred probing.
  *
  * Returns zero if the driver registered and bound to a device, else returns
  * a negative error code and with the driver not registered.
@@ -576,6 +579,12 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv,
 {
        int retval, code;
 
+       /*
+        * Prevent driver from requesting probe deferral to avoid further
+        * futile probe attempts.
+        */
+       drv->prevent_deferred_probe = true;
+
        /* make sure driver won't have bind/unbind attributes */
        drv->driver.suppress_bind_attrs = true;
 
index 90ee350442a99d243606c97b716af75d909308c9..e15430a82e90e766163c7f5f40e26559b3fabbe1 100644 (file)
@@ -30,28 +30,37 @@ static ssize_t manuf_show(struct device *dev, struct device_attribute *attr, cha
        struct bcma_device *core = container_of(dev, struct bcma_device, dev);
        return sprintf(buf, "0x%03X\n", core->id.manuf);
 }
+static DEVICE_ATTR_RO(manuf);
+
 static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct bcma_device *core = container_of(dev, struct bcma_device, dev);
        return sprintf(buf, "0x%03X\n", core->id.id);
 }
+static DEVICE_ATTR_RO(id);
+
 static ssize_t rev_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct bcma_device *core = container_of(dev, struct bcma_device, dev);
        return sprintf(buf, "0x%02X\n", core->id.rev);
 }
+static DEVICE_ATTR_RO(rev);
+
 static ssize_t class_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct bcma_device *core = container_of(dev, struct bcma_device, dev);
        return sprintf(buf, "0x%X\n", core->id.class);
 }
-static struct device_attribute bcma_device_attrs[] = {
-       __ATTR_RO(manuf),
-       __ATTR_RO(id),
-       __ATTR_RO(rev),
-       __ATTR_RO(class),
-       __ATTR_NULL,
+static DEVICE_ATTR_RO(class);
+
+static struct attribute *bcma_device_attrs[] = {
+       &dev_attr_manuf.attr,
+       &dev_attr_id.attr,
+       &dev_attr_rev.attr,
+       &dev_attr_class.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(bcma_device);
 
 static struct bus_type bcma_bus_type = {
        .name           = "bcma",
@@ -59,7 +68,7 @@ static struct bus_type bcma_bus_type = {
        .probe          = bcma_device_probe,
        .remove         = bcma_device_remove,
        .uevent         = bcma_device_uevent,
-       .dev_attrs      = bcma_device_attrs,
+       .dev_groups     = bcma_device_groups,
 };
 
 static u16 bcma_cc_core_id(struct bcma_bus *bus)
index 448ce5e29c561bcf4fb8d1a6d3013fb6339e69be..dca5834685cf93d0cd105e66a7cf656329fc3e2e 100644 (file)
@@ -486,8 +486,7 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
                }
 
                sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
-               irq_flags = devp->hd_flags & HPET_SHARED_IRQ
-                                               ? IRQF_SHARED : IRQF_DISABLED;
+               irq_flags = devp->hd_flags & HPET_SHARED_IRQ ? IRQF_SHARED : 0;
                if (request_irq(irq, hpet_interrupt, irq_flags,
                                devp->hd_name, (void *)devp)) {
                        printk(KERN_ERR "hpet: IRQ %d is not free\n", irq);
@@ -971,8 +970,6 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data)
                struct acpi_resource_fixed_memory32 *fixmem32;
 
                fixmem32 = &res->data.fixed_memory32;
-               if (!fixmem32)
-                       return AE_NO_MEMORY;
 
                hdp->hd_phys_address = fixmem32->address;
                hdp->hd_address = ioremap(fixmem32->address,
index 190d4423653f6b99690967ba3602915eda5463fb..2f685f6eda4868195d9dcc3ac7d317e9cc2fc42a 100644 (file)
@@ -193,8 +193,8 @@ int misc_register(struct miscdevice * misc)
        if (misc->minor == MISC_DYNAMIC_MINOR) {
                int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
                if (i >= DYNAMIC_MINORS) {
-                       mutex_unlock(&misc_mtx);
-                       return -EBUSY;
+                       err = -EBUSY;
+                       goto out;
                }
                misc->minor = DYNAMIC_MINORS - i - 1;
                set_bit(i, misc_minors);
@@ -203,8 +203,8 @@ int misc_register(struct miscdevice * misc)
 
                list_for_each_entry(c, &misc_list, list) {
                        if (c->minor == misc->minor) {
-                               mutex_unlock(&misc_mtx);
-                               return -EBUSY;
+                               err = -EBUSY;
+                               goto out;
                        }
                }
        }
index cfdfe493c6af007a4d2455ab0315ef2611d8ebb3..1fd00dc0689721ae967af86aa2c88dbb903ddef2 100644 (file)
@@ -220,7 +220,7 @@ static int __init nwbutton_init(void)
                return -EBUSY;
        }
 
-       if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, IRQF_DISABLED,
+       if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, 0,
                        "nwbutton", NULL)) {
                printk (KERN_WARNING "nwbutton: IRQ %d is not free.\n",
                                IRQ_NETWINDER_BUTTON);
index c0cbbd429bdc7abcf0d3055404224bff7d367196..35259961cc38f74bbf0f6727dc205f5ec03368da 100644 (file)
@@ -227,7 +227,7 @@ static inline unsigned char rtc_is_updating(void)
 
 #ifdef RTC_IRQ
 /*
- *     A very tiny interrupt handler. It runs with IRQF_DISABLED set,
+ *     A very tiny interrupt handler. It runs with interrupts disabled,
  *     but there is possibility of conflicting with the set_rtc_mmss()
  *     call (the rtc irq and the timer irq can easily run at the same
  *     time in two different CPUs). So we need to serialize
@@ -1040,8 +1040,7 @@ no_irq:
                rtc_int_handler_ptr = rtc_interrupt;
        }
 
-       if (request_irq(RTC_IRQ, rtc_int_handler_ptr, IRQF_DISABLED,
-                       "rtc", NULL)) {
+       if (request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)) {
                /* Yeah right, seeing as irq 8 doesn't even hit the bus. */
                rtc_has_irq = 0;
                printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
index 5816b39ff5a9cf43cbba56579d9b0b2d021f8883..8bab59292a0d0a42f2a89914edc36824483160f0 100644 (file)
@@ -108,8 +108,7 @@ scdrv_open(struct inode *inode, struct file *file)
        /* hook this subchannel up to the system controller interrupt */
        mutex_lock(&scdrv_mutex);
        rv = request_irq(SGI_UART_VECTOR, scdrv_interrupt,
-                        IRQF_SHARED | IRQF_DISABLED,
-                        SYSCTL_BASENAME, sd);
+                        IRQF_SHARED, SYSCTL_BASENAME, sd);
        if (rv) {
                ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch);
                kfree(sd);
index ee156948b9f810d3cd66fbb3ff303e218f884a53..59bcefd6ec7c8ba86ceacce796af03fdce7e99ad 100644 (file)
@@ -292,8 +292,7 @@ scdrv_event_init(struct sysctl_data_s *scd)
 
        /* hook event subchannel up to the system controller interrupt */
        rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt,
-                        IRQF_SHARED | IRQF_DISABLED,
-                        "system controller events", event_sd);
+                        IRQF_SHARED, "system controller events", event_sd);
        if (rv) {
                printk(KERN_WARNING "%s: irq request failed (%d)\n",
                       __func__, rv);
index e95e0ab0bd870a4c8d679d7dbb092ea2521ee42e..100cd1de9939dc77c5c523b10e15e1bc80908449 100644 (file)
@@ -222,7 +222,7 @@ static int tlclk_open(struct inode *inode, struct file *filp)
        /* This device is wired through the FPGA IO space of the ATCA blade
         * we can't share this IRQ */
        result = request_irq(telclk_interrupt, &tlclk_interrupt,
-                            IRQF_DISABLED, "telco_clock", tlclk_interrupt);
+                            0, "telco_clock", tlclk_interrupt);
        if (result == -EBUSY)
                printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n");
        else
index 5224da5202d3497589d4d74df171cde3efd7ba67..f6345f932e46c8e841ae2f7e9777da0ed9a905f3 100644 (file)
@@ -721,7 +721,7 @@ static int hwicap_remove(struct device *dev)
 {
        struct hwicap_drvdata *drvdata;
 
-       drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev);
+       drvdata = dev_get_drvdata(dev);
 
        if (!drvdata)
                return 0;
@@ -731,7 +731,6 @@ static int hwicap_remove(struct device *dev)
        iounmap(drvdata->base_address);
        release_mem_region(drvdata->mem_start, drvdata->mem_size);
        kfree(drvdata);
-       dev_set_drvdata(dev, NULL);
 
        mutex_lock(&icap_sem);
        probed_devices[MINOR(dev->devt)-XHWICAP_MINOR] = 0;
index 51410c2ac2cb617b9a98b9c0fd869e1ddd541870..4d978a3c88f743faac72af94b27cd72070507bf9 100644 (file)
  */
 
 #define SRC_CR                 0x00U
+#define SRC_CR_T0_ENSEL                BIT(15)
+#define SRC_CR_T1_ENSEL                BIT(17)
+#define SRC_CR_T2_ENSEL                BIT(19)
+#define SRC_CR_T3_ENSEL                BIT(21)
+#define SRC_CR_T4_ENSEL                BIT(23)
+#define SRC_CR_T5_ENSEL                BIT(25)
+#define SRC_CR_T6_ENSEL                BIT(27)
+#define SRC_CR_T7_ENSEL                BIT(29)
 #define SRC_XTALCR             0x0CU
 #define SRC_XTALCR_XTALTIMEN   BIT(20)
 #define SRC_XTALCR_SXTALDIS    BIT(19)
@@ -543,6 +551,19 @@ void __init nomadik_clk_init(void)
                       __func__, np->name);
                return;
        }
+
+       /* Set all timers to use the 2.4 MHz TIMCLK */
+       val = readl(src_base + SRC_CR);
+       val |= SRC_CR_T0_ENSEL;
+       val |= SRC_CR_T1_ENSEL;
+       val |= SRC_CR_T2_ENSEL;
+       val |= SRC_CR_T3_ENSEL;
+       val |= SRC_CR_T4_ENSEL;
+       val |= SRC_CR_T5_ENSEL;
+       val |= SRC_CR_T6_ENSEL;
+       val |= SRC_CR_T7_ENSEL;
+       writel(val, src_base + SRC_CR);
+
        val = readl(src_base + SRC_XTALCR);
        pr_info("SXTALO is %s\n",
                (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled");
index fc777bdc1886586d2f7e0e253ddc1e78758d4927..81a202d12a7ad89ab56909fce6782baa7fb94ec2 100644 (file)
@@ -39,8 +39,8 @@ static const struct coreclk_ratio a370_coreclk_ratios[] __initconst = {
 };
 
 static const u32 a370_tclk_freqs[] __initconst = {
-       16600000,
-       20000000,
+       166000000,
+       200000000,
 };
 
 static u32 __init a370_get_tclk_freq(void __iomem *sar)
index 5bb848cac6ece40ab0e5bceef82d4facafdbd80d..81dd31a686df9e467b7c111f9c808ee568139551 100644 (file)
@@ -49,7 +49,7 @@
 #define SOCFPGA_L4_SP_CLK              "l4_sp_clk"
 #define SOCFPGA_NAND_CLK               "nand_clk"
 #define SOCFPGA_NAND_X_CLK             "nand_x_clk"
-#define SOCFPGA_MMC_CLK                        "mmc_clk"
+#define SOCFPGA_MMC_CLK                        "sdmmc_clk"
 #define SOCFPGA_DB_CLK                 "gpio_db_clk"
 
 #define div_mask(width)        ((1 << (width)) - 1)
index 67ccf4aa72773520baa89708c30737c3ad13c493..f5e4c21b301f6438c32d3552cae4bb9aef9d2a44 100644 (file)
@@ -107,7 +107,7 @@ static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
 
        vco = icst_hz_to_vco(icst->params, rate);
        icst->rate = icst_hz(icst->params, vco);
-       vco_set(icst->vcoreg, icst->lockreg, vco);
+       vco_set(icst->lockreg, icst->vcoreg, vco);
        return 0;
 }
 
index 08ae128cce9be2e930454088c1041478bce8d8f5..c73fc2b74de2a1dd0665bde5693b1fce03f6d6c5 100644 (file)
@@ -65,6 +65,7 @@ void proc_fork_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -80,6 +81,7 @@ void proc_fork_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        /*  If cn_netlink_send() failed, the data is not sent */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
@@ -96,6 +98,7 @@ void proc_exec_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -106,6 +109,7 @@ void proc_exec_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -122,6 +126,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        ev->what = which_id;
        ev->event_data.id.process_pid = task->pid;
        ev->event_data.id.process_tgid = task->tgid;
@@ -145,6 +150,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -160,6 +166,7 @@ void proc_sid_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -170,6 +177,7 @@ void proc_sid_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -185,6 +193,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -203,6 +212,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -218,6 +228,7 @@ void proc_comm_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -229,6 +240,7 @@ void proc_comm_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -244,6 +256,7 @@ void proc_coredump_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -254,6 +267,7 @@ void proc_coredump_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -269,6 +283,7 @@ void proc_exit_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -281,6 +296,7 @@ void proc_exit_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -304,6 +320,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        msg->seq = rcvd_seq;
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -313,6 +330,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = rcvd_ack + 1;
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
index 6ecfa758942c50a4b33ebd399cd831ac1420277d..a36749f1e44a869418e1bcab6481334948618391 100644 (file)
@@ -109,7 +109,7 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
 
        data = nlmsg_data(nlh);
 
-       memcpy(data, msg, sizeof(*data) + msg->len);
+       memcpy(data, msg, size);
 
        NETLINK_CB(skb).dst_group = group;
 
@@ -157,17 +157,18 @@ static int cn_call_callback(struct sk_buff *skb)
 static void cn_rx_skb(struct sk_buff *__skb)
 {
        struct nlmsghdr *nlh;
-       int err;
        struct sk_buff *skb;
+       int len, err;
 
        skb = skb_get(__skb);
 
        if (skb->len >= NLMSG_HDRLEN) {
                nlh = nlmsg_hdr(skb);
+               len = nlmsg_len(nlh);
 
-               if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
+               if (len < (int)sizeof(struct cn_msg) ||
                    skb->len < nlh->nlmsg_len ||
-                   nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) {
+                   len > CONNECTOR_MAX_MSG_SIZE) {
                        kfree_skb(skb);
                        return;
                }
index d2c3253e015ee23f107d2d34333c6afa533ab7cf..506fd23c755069f1f1b1e81763335f8f79b0de37 100644 (file)
@@ -986,12 +986,12 @@ static int __init acpi_cpufreq_init(void)
 {
        int ret;
 
+       if (acpi_disabled)
+               return -ENODEV;
+
        /* don't keep reloading if cpufreq_driver exists */
        if (cpufreq_get_current_driver())
-               return 0;
-
-       if (acpi_disabled)
-               return 0;
+               return -EEXIST;
 
        pr_debug("acpi_cpufreq_init\n");
 
index badf6206b2b20d7284ffbbd098fd66676937fb64..eb3fdc755000e0005f43474bb5ae967dd6ab8aa8 100644 (file)
@@ -48,7 +48,7 @@ static inline int32_t div_fp(int32_t x, int32_t y)
 }
 
 struct sample {
-       int core_pct_busy;
+       int32_t core_pct_busy;
        u64 aperf;
        u64 mperf;
        int freq;
@@ -68,7 +68,7 @@ struct _pid {
        int32_t i_gain;
        int32_t d_gain;
        int deadband;
-       int last_err;
+       int32_t last_err;
 };
 
 struct cpudata {
@@ -153,16 +153,15 @@ static inline void pid_d_gain_set(struct _pid *pid, int percent)
        pid->d_gain = div_fp(int_tofp(percent), int_tofp(100));
 }
 
-static signed int pid_calc(struct _pid *pid, int busy)
+static signed int pid_calc(struct _pid *pid, int32_t busy)
 {
-       signed int err, result;
+       signed int result;
        int32_t pterm, dterm, fp_error;
        int32_t integral_limit;
 
-       err = pid->setpoint - busy;
-       fp_error = int_tofp(err);
+       fp_error = int_tofp(pid->setpoint) - busy;
 
-       if (abs(err) <= pid->deadband)
+       if (abs(fp_error) <= int_tofp(pid->deadband))
                return 0;
 
        pterm = mul_fp(pid->p_gain, fp_error);
@@ -176,8 +175,8 @@ static signed int pid_calc(struct _pid *pid, int busy)
        if (pid->integral < -integral_limit)
                pid->integral = -integral_limit;
 
-       dterm = mul_fp(pid->d_gain, (err - pid->last_err));
-       pid->last_err = err;
+       dterm = mul_fp(pid->d_gain, fp_error - pid->last_err);
+       pid->last_err = fp_error;
 
        result = pterm + mul_fp(pid->integral, pid->i_gain) + dterm;
 
@@ -367,12 +366,13 @@ static int intel_pstate_turbo_pstate(void)
 static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max)
 {
        int max_perf = cpu->pstate.turbo_pstate;
+       int max_perf_adj;
        int min_perf;
        if (limits.no_turbo)
                max_perf = cpu->pstate.max_pstate;
 
-       max_perf = fp_toint(mul_fp(int_tofp(max_perf), limits.max_perf));
-       *max = clamp_t(int, max_perf,
+       max_perf_adj = fp_toint(mul_fp(int_tofp(max_perf), limits.max_perf));
+       *max = clamp_t(int, max_perf_adj,
                        cpu->pstate.min_pstate, cpu->pstate.turbo_pstate);
 
        min_perf = fp_toint(mul_fp(int_tofp(max_perf), limits.min_perf));
@@ -436,8 +436,9 @@ static inline void intel_pstate_calc_busy(struct cpudata *cpu,
                                        struct sample *sample)
 {
        u64 core_pct;
-       core_pct = div64_u64(sample->aperf * 100, sample->mperf);
-       sample->freq = cpu->pstate.max_pstate * core_pct * 1000;
+       core_pct = div64_u64(int_tofp(sample->aperf * 100),
+                            sample->mperf);
+       sample->freq = fp_toint(cpu->pstate.max_pstate * core_pct * 1000);
 
        sample->core_pct_busy = core_pct;
 }
@@ -469,22 +470,19 @@ static inline void intel_pstate_set_sample_time(struct cpudata *cpu)
        mod_timer_pinned(&cpu->timer, jiffies + delay);
 }
 
-static inline int intel_pstate_get_scaled_busy(struct cpudata *cpu)
+static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
 {
-       int32_t busy_scaled;
        int32_t core_busy, max_pstate, current_pstate;
 
-       core_busy = int_tofp(cpu->samples[cpu->sample_ptr].core_pct_busy);
+       core_busy = cpu->samples[cpu->sample_ptr].core_pct_busy;
        max_pstate = int_tofp(cpu->pstate.max_pstate);
        current_pstate = int_tofp(cpu->pstate.current_pstate);
-       busy_scaled = mul_fp(core_busy, div_fp(max_pstate, current_pstate));
-
-       return fp_toint(busy_scaled);
+       return mul_fp(core_busy, div_fp(max_pstate, current_pstate));
 }
 
 static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
 {
-       int busy_scaled;
+       int32_t busy_scaled;
        struct _pid *pid;
        signed int ctl = 0;
        int steps;
index 3519111c566b8a3bc9a43b8b89bc2b866c3d833e..10b577fcf48d82dfd70f6c120d379eaa2f24e2aa 100644 (file)
@@ -305,6 +305,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
                                edma_alloc_slot(EDMA_CTLR(echan->ch_num),
                                                EDMA_SLOT_ANY);
                        if (echan->slot[i] < 0) {
+                               kfree(edesc);
                                dev_err(dev, "Failed to allocate slot\n");
                                kfree(edesc);
                                return NULL;
@@ -346,6 +347,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
                        ccnt = sg_dma_len(sg) / (acnt * bcnt);
                        if (ccnt > (SZ_64K - 1)) {
                                dev_err(dev, "Exceeded max SG segment size\n");
+                               kfree(edesc);
                                return NULL;
                        }
                        cidx = acnt * bcnt;
index 5985807e52c9439fc1f7587aa2c0645f5e344c57..e23f1c2e505366dd24cb2ff6037f31074f7331ff 100644 (file)
 
 /**
  * struct adc_jack_data - internal data for adc_jack device driver
- * @edev        - extcon device.
- * @cable_names - list of supported cables.
- * @num_cables  - size of cable_names.
- * @adc_conditions       - list of adc value conditions.
- * @num_conditions       - size of adc_conditions.
- * @irq         - irq number of attach/detach event (0 if not exist).
- * @handling_delay      - interrupt handler will schedule extcon event
- *                      handling at handling_delay jiffies.
- * @handler     - extcon event handler called by interrupt handler.
- * @chan       - iio channel being queried.
+ * @edev:              extcon device.
+ * @cable_names:       list of supported cables.
+ * @num_cables:                size of cable_names.
+ * @adc_conditions:    list of adc value conditions.
+ * @num_conditions:    size of adc_conditions.
+ * @irq:               irq number of attach/detach event (0 if not exist).
+ * @handling_delay:    interrupt handler will schedule extcon event
+ *                     handling at handling_delay jiffies.
+ * @handler:           extcon event handler called by interrupt handler.
+ * @chan:              iio channel being queried.
  */
 struct adc_jack_data {
        struct extcon_dev edev;
@@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work)
 
        ret = iio_read_channel_raw(data->chan, &adc_val);
        if (ret < 0) {
-               dev_err(data->edev.dev, "read channel() error: %d\n", ret);
+               dev_err(&data->edev.dev, "read channel() error: %d\n", ret);
                return;
        }
 
@@ -95,7 +95,7 @@ static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
 static int adc_jack_probe(struct platform_device *pdev)
 {
        struct adc_jack_data *data;
-       struct adc_jack_pdata *pdata = pdev->dev.platform_data;
+       struct adc_jack_pdata *pdata = dev_get_platdata(&pdev->dev);
        int i, err = 0;
 
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
@@ -110,6 +110,7 @@ static int adc_jack_probe(struct platform_device *pdev)
                goto out;
        }
 
+       data->edev.dev.parent = &pdev->dev;
        data->edev.supported_cable = pdata->cable_names;
 
        /* Check the length of array and set num_cables */
@@ -148,7 +149,7 @@ static int adc_jack_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, data);
 
-       err = extcon_dev_register(&data->edev, &pdev->dev);
+       err = extcon_dev_register(&data->edev);
        if (err)
                goto out;
 
index e55713083c7887abe7baa60d24396126446bbf30..3c55ec856e39c714e7b474f081c01180b9aa9e2d 100644 (file)
@@ -86,8 +86,8 @@ struct arizona_extcon_info {
 };
 
 static const struct arizona_micd_config micd_default_modes[] = {
-       { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
-       { 0,                  2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
+       { ARIZONA_ACCDET_SRC, 1, 0 },
+       { 0,                  2, 1 },
 };
 
 static const struct arizona_micd_range micd_default_ranges[] = {
@@ -182,7 +182,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
                                        info->micd_modes[mode].gpio);
        regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
                           ARIZONA_MICD_BIAS_SRC_MASK,
-                          info->micd_modes[mode].bias);
+                          info->micd_modes[mode].bias <<
+                          ARIZONA_MICD_BIAS_SRC_SHIFT);
        regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
                           ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
 
@@ -193,7 +194,7 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
 
 static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info)
 {
-       switch (info->micd_modes[0].bias >> ARIZONA_MICD_BIAS_SRC_SHIFT) {
+       switch (info->micd_modes[0].bias) {
        case 1:
                return "MICBIAS1";
        case 2:
@@ -388,7 +389,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                           >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
 
                if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 &&
-                   (val < 100 || val > 0x3fb)) {
+                   (val < 100 || val >= 0x3fb)) {
                        range++;
                        dev_dbg(arizona->dev, "Moving to HPDET range %d\n",
                                range);
@@ -401,7 +402,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                }
 
                /* If we go out of range report top of range */
-               if (val < 100 || val > 0x3fb) {
+               if (val < 100 || val >= 0x3fb) {
                        dev_dbg(arizona->dev, "Measurement out of range\n");
                        return ARIZONA_HPDET_MAX;
                }
@@ -514,7 +515,7 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
                }
 
                /*
-                * If we measure the mic as 
+                * If we measure the mic as high impedance
                 */
                if (!id_gpio || info->hpdet_res[1] > 50) {
                        dev_dbg(arizona->dev, "Detected mic\n");
@@ -564,11 +565,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        }
 
        ret = arizona_hpdet_read(info);
-       if (ret == -EAGAIN) {
+       if (ret == -EAGAIN)
                goto out;
-       } else if (ret < 0) {
+       else if (ret < 0)
                goto done;
-       }
        reading = ret;
 
        /* Reset back to starting range */
@@ -578,11 +578,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
                           0);
 
        ret = arizona_hpdet_do_id(info, &reading, &mic);
-       if (ret == -EAGAIN) {
+       if (ret == -EAGAIN)
                goto out;
-       } else if (ret < 0) {
+       else if (ret < 0)
                goto done;
-       }
 
        /* Report high impedence cables as line outputs */
        if (reading >= 5000)
@@ -738,8 +737,8 @@ err:
 static void arizona_micd_timeout_work(struct work_struct *work)
 {
        struct arizona_extcon_info *info = container_of(work,
-                                                       struct arizona_extcon_info,
-                                                       micd_timeout_work.work);
+                                               struct arizona_extcon_info,
+                                               micd_timeout_work.work);
 
        mutex_lock(&info->lock);
 
@@ -756,8 +755,8 @@ static void arizona_micd_timeout_work(struct work_struct *work)
 static void arizona_micd_detect(struct work_struct *work)
 {
        struct arizona_extcon_info *info = container_of(work,
-                                                       struct arizona_extcon_info,
-                                                       micd_detect_work.work);
+                                               struct arizona_extcon_info,
+                                               micd_detect_work.work);
        struct arizona *arizona = info->arizona;
        unsigned int val = 0, lvl;
        int ret, i, key;
@@ -769,7 +768,8 @@ static void arizona_micd_detect(struct work_struct *work)
        for (i = 0; i < 10 && !(val & 0x7fc); i++) {
                ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
                if (ret != 0) {
-                       dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
+                       dev_err(arizona->dev,
+                               "Failed to read MICDET: %d\n", ret);
                        mutex_unlock(&info->lock);
                        return;
                }
@@ -777,7 +777,8 @@ static void arizona_micd_detect(struct work_struct *work)
                dev_dbg(arizona->dev, "MICDET: %x\n", val);
 
                if (!(val & ARIZONA_MICD_VALID)) {
-                       dev_warn(arizona->dev, "Microphone detection state invalid\n");
+                       dev_warn(arizona->dev,
+                                "Microphone detection state invalid\n");
                        mutex_unlock(&info->lock);
                        return;
                }
@@ -925,8 +926,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)
 static void arizona_hpdet_work(struct work_struct *work)
 {
        struct arizona_extcon_info *info = container_of(work,
-                                                       struct arizona_extcon_info,
-                                                       hpdet_work.work);
+                                               struct arizona_extcon_info,
+                                               hpdet_work.work);
 
        mutex_lock(&info->lock);
        arizona_start_hpdet_acc_id(info);
@@ -973,10 +974,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                                           &info->hpdet_work,
                                           msecs_to_jiffies(HPDET_DEBOUNCE));
 
-               if (cancelled_mic)
+               if (cancelled_mic) {
+                       int micd_timeout = info->micd_timeout;
+
                        queue_delayed_work(system_power_efficient_wq,
                                           &info->micd_timeout_work,
-                                          msecs_to_jiffies(info->micd_timeout));
+                                          msecs_to_jiffies(micd_timeout));
+               }
 
                goto out;
        }
@@ -1039,6 +1043,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
        else
                info->micd_timeout = DEFAULT_MICD_TIMEOUT;
 
+out:
        /* Clear trig_sts to make sure DCVDD is not forced up */
        regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
                     ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
@@ -1046,7 +1051,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                     ARIZONA_JD1_FALL_TRIG_STS |
                     ARIZONA_JD1_RISE_TRIG_STS);
 
-out:
        mutex_unlock(&info->lock);
 
        pm_runtime_mark_last_busy(info->dev);
@@ -1129,9 +1133,10 @@ static int arizona_extcon_probe(struct platform_device *pdev)
        }
 
        info->edev.name = "Headset Jack";
+       info->edev.dev.parent = arizona->dev;
        info->edev.supported_cable = arizona_cable;
 
-       ret = extcon_dev_register(&info->edev, arizona->dev);
+       ret = extcon_dev_register(&info->edev);
        if (ret < 0) {
                dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
                        ret);
index 148382faded9f125940ee11ca211bcea38f36290..15443d3b6be18049ddddbb7ec035eb05ce7dc409 100644 (file)
@@ -74,7 +74,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock);
 
 /**
  * check_mutually_exclusive - Check if new_state violates mutually_exclusive
- *                         condition.
+ *                           condition.
  * @edev:      the extcon device
  * @new_state: new cable attach status for @edev
  *
@@ -105,7 +105,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
                          char *buf)
 {
        int i, count = 0;
-       struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+       struct extcon_dev *edev = dev_get_drvdata(dev);
 
        if (edev->print_state) {
                int ret = edev->print_state(edev, buf);
@@ -129,13 +129,12 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
-int extcon_set_state(struct extcon_dev *edev, u32 state);
 static ssize_t state_store(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t count)
 {
        u32 state;
        ssize_t ret = 0;
-       struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+       struct extcon_dev *edev = dev_get_drvdata(dev);
 
        ret = sscanf(buf, "0x%x", &state);
        if (ret == 0)
@@ -153,7 +152,7 @@ static DEVICE_ATTR_RW(state);
 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
                char *buf)
 {
-       struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+       struct extcon_dev *edev = dev_get_drvdata(dev);
 
        /* Optional callback given by the user */
        if (edev->print_name) {
@@ -162,7 +161,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
                        return ret;
        }
 
-       return sprintf(buf, "%s\n", dev_name(edev->dev));
+       return sprintf(buf, "%s\n", dev_name(&edev->dev));
 }
 static DEVICE_ATTR_RO(name);
 
@@ -189,7 +188,7 @@ static ssize_t cable_state_show(struct device *dev,
 
 /**
  * extcon_update_state() - Update the cable attach states of the extcon device
- *                     only for the masked bits.
+ *                        only for the masked bits.
  * @edev:      the extcon device
  * @mask:      the bit mask to designate updated bits.
  * @state:     new cable attach status for @edev
@@ -227,11 +226,10 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
                edev->state |= state & mask;
 
                raw_notifier_call_chain(&edev->nh, old_state, edev);
-
                /* This could be in interrupt handler */
                prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
                if (prop_buf) {
-                       length = name_show(edev->dev, NULL, prop_buf);
+                       length = name_show(&edev->dev, NULL, prop_buf);
                        if (length > 0) {
                                if (prop_buf[length - 1] == '\n')
                                        prop_buf[length - 1] = 0;
@@ -239,7 +237,7 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
                                        "NAME=%s", prop_buf);
                                envp[env_offset++] = name_buf;
                        }
-                       length = state_show(edev->dev, NULL, prop_buf);
+                       length = state_show(&edev->dev, NULL, prop_buf);
                        if (length > 0) {
                                if (prop_buf[length - 1] == '\n')
                                        prop_buf[length - 1] = 0;
@@ -251,14 +249,14 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
                        /* Unlock early before uevent */
                        spin_unlock_irqrestore(&edev->lock, flags);
 
-                       kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
+                       kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
                        free_page((unsigned long)prop_buf);
                } else {
                        /* Unlock early before uevent */
                        spin_unlock_irqrestore(&edev->lock, flags);
 
-                       dev_err(edev->dev, "out of memory in extcon_set_state\n");
-                       kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
+                       dev_err(&edev->dev, "out of memory in extcon_set_state\n");
+                       kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
                }
        } else {
                /* No changes */
@@ -339,8 +337,9 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state);
 
 /**
  * extcon_set_cable_state_() - Set the status of a specific cable.
- * @edev:      the extcon device that has the cable.
- * @index:     cable index that can be retrieved by extcon_find_cable_index().
+ * @edev:              the extcon device that has the cable.
+ * @index:             cable index that can be retrieved by
+ *                     extcon_find_cable_index().
  * @cable_state:       the new cable status. The default semantics is
  *                     true: attached / false: detached.
  */
@@ -359,8 +358,8 @@ EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
 
 /**
  * extcon_set_cable_state() - Set the status of a specific cable.
- * @edev:      the extcon device that has the cable.
- * @cable_name:        cable name.
+ * @edev:              the extcon device that has the cable.
+ * @cable_name:                cable name.
  * @cable_state:       the new cable status. The default semantics is
  *                     true: attached / false: detached.
  *
@@ -419,14 +418,14 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val,
 
 /**
  * extcon_register_interest() - Register a notifier for a state change of a
- *                           specific cable, not an entier set of cables of a
- *                           extcon device.
- * @obj:       an empty extcon_specific_cable_nb object to be returned.
+ *                             specific cable, not an entier set of cables of a
+ *                             extcon device.
+ * @obj:               an empty extcon_specific_cable_nb object to be returned.
  * @extcon_name:       the name of extcon device.
  *                     if NULL, extcon_register_interest will register
  *                     every cable with the target cable_name given.
  * @cable_name:                the target cable name.
- * @nb:                the notifier block to get notified.
+ * @nb:                        the notifier block to get notified.
  *
  * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
  * the struct for you.
@@ -452,7 +451,8 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
                if (!obj->edev)
                        return -ENODEV;
 
-               obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
+               obj->cable_index = extcon_find_cable_index(obj->edev,
+                                                         cable_name);
                if (obj->cable_index < 0)
                        return obj->cable_index;
 
@@ -460,7 +460,8 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
 
                obj->internal_nb.notifier_call = _call_per_cable;
 
-               return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb);
+               return raw_notifier_chain_register(&obj->edev->nh,
+                                                 &obj->internal_nb);
        } else {
                struct class_dev_iter iter;
                struct extcon_dev *extd;
@@ -470,7 +471,7 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
                        return -ENODEV;
                class_dev_iter_init(&iter, extcon_class, NULL, NULL);
                while ((dev = class_dev_iter_next(&iter))) {
-                       extd = (struct extcon_dev *)dev_get_drvdata(dev);
+                       extd = dev_get_drvdata(dev);
 
                        if (extcon_find_cable_index(extd, cable_name) < 0)
                                continue;
@@ -487,7 +488,7 @@ EXPORT_SYMBOL_GPL(extcon_register_interest);
 
 /**
  * extcon_unregister_interest() - Unregister the notifier registered by
- *                             extcon_register_interest().
+ *                               extcon_register_interest().
  * @obj:       the extcon_specific_cable_nb object returned by
  *             extcon_register_interest().
  */
@@ -502,7 +503,7 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);
 
 /**
  * extcon_register_notifier() - Register a notifiee to get notified by
- *                           any attach status changes from the extcon.
+ *                             any attach status changes from the extcon.
  * @edev:      the extcon device.
  * @nb:                a notifier block to be registered.
  *
@@ -556,7 +557,6 @@ static int create_extcon_class(void)
 
 static void extcon_dev_release(struct device *dev)
 {
-       kfree(dev);
 }
 
 static const char *muex_name = "mutually_exclusive";
@@ -567,14 +567,13 @@ static void dummy_sysfs_dev_release(struct device *dev)
 /**
  * extcon_dev_register() - Register a new extcon device
  * @edev       : the new extcon device (should be allocated before calling)
- * @dev                : the parent device for this extcon device.
  *
  * Among the members of edev struct, please set the "user initializing data"
  * in any case and set the "optional callbacks" if required. However, please
  * do not set the values of "internal data", which are initialized by
  * this function.
  */
-int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
+int extcon_dev_register(struct extcon_dev *edev)
 {
        int ret, index = 0;
 
@@ -594,19 +593,20 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
        }
 
        if (index > SUPPORTED_CABLE_MAX) {
-               dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n");
+               dev_err(&edev->dev, "extcon: maximum number of supported cables exceeded.\n");
                return -EINVAL;
        }
 
-       edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
-       if (!edev->dev)
-               return -ENOMEM;
-       edev->dev->parent = dev;
-       edev->dev->class = extcon_class;
-       edev->dev->release = extcon_dev_release;
+       edev->dev.class = extcon_class;
+       edev->dev.release = extcon_dev_release;
 
-       edev->name = edev->name ? edev->name : dev_name(dev);
-       dev_set_name(edev->dev, "%s", edev->name);
+       edev->name = edev->name ? edev->name : dev_name(edev->dev.parent);
+       if (IS_ERR_OR_NULL(edev->name)) {
+               dev_err(&edev->dev,
+                       "extcon device name is null\n");
+               return -EINVAL;
+       }
+       dev_set_name(&edev->dev, "%s", edev->name);
 
        if (edev->max_supported) {
                char buf[10];
@@ -714,7 +714,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
                        goto err_alloc_groups;
                }
 
-               edev->extcon_dev_type.name = dev_name(edev->dev);
+               edev->extcon_dev_type.name = dev_name(&edev->dev);
                edev->extcon_dev_type.release = dummy_sysfs_dev_release;
 
                for (index = 0; index < edev->max_supported; index++)
@@ -724,25 +724,24 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
                        edev->extcon_dev_type.groups[index] =
                                &edev->attr_g_muex;
 
-               edev->dev->type = &edev->extcon_dev_type;
+               edev->dev.type = &edev->extcon_dev_type;
        }
 
-       ret = device_register(edev->dev);
+       ret = device_register(&edev->dev);
        if (ret) {
-               put_device(edev->dev);
+               put_device(&edev->dev);
                goto err_dev;
        }
 #if defined(CONFIG_ANDROID)
        if (switch_class)
-               ret = class_compat_create_link(switch_class, edev->dev,
-                                              NULL);
+               ret = class_compat_create_link(switch_class, &edev->dev, NULL);
 #endif /* CONFIG_ANDROID */
 
        spin_lock_init(&edev->lock);
 
        RAW_INIT_NOTIFIER_HEAD(&edev->nh);
 
-       dev_set_drvdata(edev->dev, edev);
+       dev_set_drvdata(&edev->dev, edev);
        edev->state = 0;
 
        mutex_lock(&extcon_dev_list_lock);
@@ -768,7 +767,6 @@ err_alloc_cables:
        if (edev->max_supported)
                kfree(edev->cables);
 err_sysfs_alloc:
-       kfree(edev->dev);
        return ret;
 }
 EXPORT_SYMBOL_GPL(extcon_dev_register);
@@ -788,9 +786,9 @@ void extcon_dev_unregister(struct extcon_dev *edev)
        list_del(&edev->entry);
        mutex_unlock(&extcon_dev_list_lock);
 
-       if (IS_ERR_OR_NULL(get_device(edev->dev))) {
-               dev_err(edev->dev, "Failed to unregister extcon_dev (%s)\n",
-                               dev_name(edev->dev));
+       if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
+               dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
+                               dev_name(&edev->dev));
                return;
        }
 
@@ -812,10 +810,10 @@ void extcon_dev_unregister(struct extcon_dev *edev)
 
 #if defined(CONFIG_ANDROID)
        if (switch_class)
-               class_compat_remove_link(switch_class, edev->dev, NULL);
+               class_compat_remove_link(switch_class, &edev->dev, NULL);
 #endif
-       device_unregister(edev->dev);
-       put_device(edev->dev);
+       device_unregister(&edev->dev);
+       put_device(&edev->dev);
 }
 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
 
index f874c30ddbff0a0bb51949d7aa2de90c67c7227b..7e0dff58e4943e8b1b09a44c4adba854898cda9d 100644 (file)
@@ -34,6 +34,7 @@
 struct gpio_extcon_data {
        struct extcon_dev edev;
        unsigned gpio;
+       bool gpio_active_low;
        const char *state_on;
        const char *state_off;
        int irq;
@@ -49,6 +50,8 @@ static void gpio_extcon_work(struct work_struct *work)
                             work);
 
        state = gpio_get_value(data->gpio);
+       if (data->gpio_active_low)
+               state = !state;
        extcon_set_state(&data->edev, state);
 }
 
@@ -78,9 +81,9 @@ static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
 
 static int gpio_extcon_probe(struct platform_device *pdev)
 {
-       struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data;
+       struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct gpio_extcon_data *extcon_data;
-       int ret = 0;
+       int ret;
 
        if (!pdata)
                return -EBUSY;
@@ -95,14 +98,22 @@ static int gpio_extcon_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        extcon_data->edev.name = pdata->name;
+       extcon_data->edev.dev.parent = &pdev->dev;
        extcon_data->gpio = pdata->gpio;
+       extcon_data->gpio_active_low = pdata->gpio_active_low;
        extcon_data->state_on = pdata->state_on;
        extcon_data->state_off = pdata->state_off;
        if (pdata->state_on && pdata->state_off)
                extcon_data->edev.print_state = extcon_gpio_print_state;
-       extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce);
+       if (pdata->debounce) {
+               ret = gpio_set_debounce(extcon_data->gpio,
+                                       pdata->debounce * 1000);
+               if (ret < 0)
+                       extcon_data->debounce_jiffies =
+                               msecs_to_jiffies(pdata->debounce);
+       }
 
-       ret = extcon_dev_register(&extcon_data->edev, &pdev->dev);
+       ret = extcon_dev_register(&extcon_data->edev);
        if (ret < 0)
                return ret;
 
index b56bdaa27d4ba15464c645184b2c18ee2df7e530..da268fbc901beae35c56c8da9930f0ddca5dc0dd 100644 (file)
@@ -189,14 +189,17 @@ enum max77693_muic_acc_type {
 
        /* The below accessories have same ADC value so ADCLow and
           ADC1K bit is used to separate specific accessory */
-       MAX77693_MUIC_GND_USB_OTG = 0x100,      /* ADC:0x0, VBVolot:0, ADCLow:0, ADC1K:0 */
-       MAX77693_MUIC_GND_USB_OTG_VB = 0x104,   /* ADC:0x0, VBVolot:1, ADCLow:0, ADC1K:0 */
-       MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* ADC:0x0, VBVolot:0, ADCLow:1, ADC1K:0 */
-       MAX77693_MUIC_GND_MHL = 0x103,          /* ADC:0x0, VBVolot:0, ADCLow:1, ADC1K:1 */
-       MAX77693_MUIC_GND_MHL_VB = 0x107,       /* ADC:0x0, VBVolot:1, ADCLow:1, ADC1K:1 */
+                                               /* ADC|VBVolot|ADCLow|ADC1K| */
+       MAX77693_MUIC_GND_USB_OTG = 0x100,      /* 0x0|      0|     0|    0| */
+       MAX77693_MUIC_GND_USB_OTG_VB = 0x104,   /* 0x0|      1|     0|    0| */
+       MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* 0x0|      0|     1|    0| */
+       MAX77693_MUIC_GND_MHL = 0x103,          /* 0x0|      0|     1|    1| */
+       MAX77693_MUIC_GND_MHL_VB = 0x107,       /* 0x0|      1|     1|    1| */
 };
 
-/* MAX77693 MUIC device support below list of accessories(external connector) */
+/*
+ * MAX77693 MUIC device support below list of accessories(external connector)
+ */
 enum {
        EXTCON_CABLE_USB = 0,
        EXTCON_CABLE_USB_HOST,
@@ -395,12 +398,12 @@ static int max77693_muic_get_cable_type(struct max77693_muic_info *info,
                        vbvolt >>= STATUS2_VBVOLT_SHIFT;
 
                        /**
-                        * [0x1][VBVolt][ADCLow][ADC1K]
-                        * [0x1    0       0       0  ] : USB_OTG
-                        * [0x1    1       0       0  ] : USB_OTG_VB
-                        * [0x1    0       1       0  ] : Audio Video Cable with load
-                        * [0x1    0       1       1  ] : MHL without charging connector
-                        * [0x1    1       1       1  ] : MHL with charging connector
+                        * [0x1|VBVolt|ADCLow|ADC1K]
+                        * [0x1|     0|     0|    0] USB_OTG
+                        * [0x1|     1|     0|    0] USB_OTG_VB
+                        * [0x1|     0|     1|    0] Audio Video cable with load
+                        * [0x1|     0|     1|    1] MHL without charging cable
+                        * [0x1|     1|     1|    1] MHL with charging cable
                         */
                        cable_type = ((0x1 << 8)
                                        | (vbvolt << 2)
@@ -723,11 +726,11 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
                if (ret < 0)
                        return ret;
                break;
-       case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:        /* DOCK_KEY_PREV */
-       case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:        /* DOCK_KEY_NEXT */
-       case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:        /* DOCK_VOL_DOWN */
-       case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:       /* DOCK_VOL_UP */
-       case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:       /* DOCK_KEY_PLAY_PAUSE */
+       case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:      /* DOCK_KEY_PREV */
+       case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:      /* DOCK_KEY_NEXT */
+       case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:      /* DOCK_VOL_DOWN */
+       case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:     /* DOCK_VOL_UP */
+       case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:     /* DOCK_KEY_PLAY_PAUSE */
                /*
                 * Button of DOCK device
                 * - the Prev/Next/Volume Up/Volume Down/Play-Pause button
@@ -815,19 +818,21 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                case MAX77693_MUIC_GND_MHL_VB:
                        /*
                         * MHL cable with MHL_TA(USB/TA) cable
-                        * - MHL cable include two port(HDMI line and separate micro-
-                        * usb port. When the target connect MHL cable, extcon driver
-                        * check whether MHL_TA(USB/TA) cable is connected. If MHL_TA
-                        * cable is connected, extcon driver notify state to notifiee
-                        * for charging battery.
+                        * - MHL cable include two port(HDMI line and separate
+                        * micro-usb port. When the target connect MHL cable,
+                        * extcon driver check whether MHL_TA(USB/TA) cable is
+                        * connected. If MHL_TA cable is connected, extcon
+                        * driver notify state to notifiee for charging battery.
                         *
                         * Features of 'MHL_TA(USB/TA) with MHL cable'
                         * - Support MHL
-                        * - Support charging through micro-usb port without data connection
+                        * - Support charging through micro-usb port without
+                        *   data connection
                         */
                        extcon_set_cable_state(info->edev, "MHL_TA", attached);
                        if (!cable_attached)
-                               extcon_set_cable_state(info->edev, "MHL", cable_attached);
+                               extcon_set_cable_state(info->edev,
+                                                     "MHL", cable_attached);
                        break;
                }
 
@@ -839,47 +844,51 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:         /* Dock-Audio */
                        /*
                         * Dock-Audio device with USB/TA cable
-                        * - Dock device include two port(Dock-Audio and micro-usb
-                        * port). When the target connect Dock-Audio device, extcon
-                        * driver check whether USB/TA cable is connected. If USB/TA
-                        * cable is connected, extcon driver notify state to notifiee
-                        * for charging battery.
+                        * - Dock device include two port(Dock-Audio and micro-
+                        * usb port). When the target connect Dock-Audio device,
+                        * extcon driver check whether USB/TA cable is connected
+                        * or not. If USB/TA cable is connected, extcon driver
+                        * notify state to notifiee for charging battery.
                         *
                         * Features of 'USB/TA cable with Dock-Audio device'
                         * - Support external output feature of audio.
-                        * - Support charging through micro-usb port without data
-                        *           connection.
+                        * - Support charging through micro-usb port without
+                        *   data connection.
                         */
                        extcon_set_cable_state(info->edev, "USB", attached);
 
                        if (!cable_attached)
-                               extcon_set_cable_state(info->edev, "Dock-Audio", cable_attached);
+                               extcon_set_cable_state(info->edev, "Dock-Audio",
+                                                     cable_attached);
                        break;
                case MAX77693_MUIC_ADC_RESERVED_ACC_3:          /* Dock-Smart */
                        /*
                         * Dock-Smart device with USB/TA cable
                         * - Dock-Desk device include three type of cable which
                         * are HDMI, USB for mouse/keyboard and micro-usb port
-                        * for USB/TA cable. Dock-Smart device need always exteranl
-                        * power supply(USB/TA cable through micro-usb cable). Dock-
-                        * Smart device support screen output of target to separate
-                        * monitor and mouse/keyboard for desktop mode.
+                        * for USB/TA cable. Dock-Smart device need always
+                        * exteranl power supply(USB/TA cable through micro-usb
+                        * cable). Dock-Smart device support screen output of
+                        * target to separate monitor and mouse/keyboard for
+                        * desktop mode.
                         *
                         * Features of 'USB/TA cable with Dock-Smart device'
                         * - Support MHL
                         * - Support external output feature of audio
-                        * - Support charging through micro-usb port without data
-                        *           connection if TA cable is connected to target.
-                        * - Support charging and data connection through micro-usb port
-                        *           if USB cable is connected between target and host
-                        *           device.
+                        * - Support charging through micro-usb port without
+                        *   data connection if TA cable is connected to target.
+                        * - Support charging and data connection through micro-
+                        *   usb port if USB cable is connected between target
+                        *   and host device
                         * - Support OTG device (Mouse/Keyboard)
                         */
-                       ret = max77693_muic_set_path(info, info->path_usb, attached);
+                       ret = max77693_muic_set_path(info, info->path_usb,
+                                                   attached);
                        if (ret < 0)
                                return ret;
 
-                       extcon_set_cable_state(info->edev, "Dock-Smart", attached);
+                       extcon_set_cable_state(info->edev, "Dock-Smart",
+                                             attached);
                        extcon_set_cable_state(info->edev, "MHL", attached);
 
                        break;
@@ -889,25 +898,28 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                switch (chg_type) {
                case MAX77693_CHARGER_TYPE_NONE:
                        /*
-                        * When MHL(with USB/TA cable) or Dock-Audio with USB/TA cable
-                        * is attached, muic device happen below two interrupt.
-                        * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting MHL/Dock-Audio.
-                        * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting USB/TA cable
-                        *   connected to MHL or Dock-Audio.
-                        * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC interrupt
-                        * than MAX77693_MUIC_IRQ_INT2_CHGTYP interrupt.
+                        * When MHL(with USB/TA cable) or Dock-Audio with USB/TA
+                        * cable is attached, muic device happen below two irq.
+                        * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting
+                        *    MHL/Dock-Audio.
+                        * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting
+                        *    USB/TA cable connected to MHL or Dock-Audio.
+                        * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC
+                        * irq than MAX77693_MUIC_IRQ_INT2_CHGTYP irq.
                         *
-                        * If user attach MHL (with USB/TA cable and immediately detach
-                        * MHL with USB/TA cable before MAX77693_MUIC_IRQ_INT2_CHGTYP
-                        * interrupt is happened, USB/TA cable remain connected state to
-                        * target. But USB/TA cable isn't connected to target. The user
-                        * be face with unusual action. So, driver should check this
-                        * situation in spite of, that previous charger type is N/A.
+                        * If user attach MHL (with USB/TA cable and immediately
+                        * detach MHL with USB/TA cable before MAX77693_MUIC_IRQ
+                        * _INT2_CHGTYP irq is happened, USB/TA cable remain
+                        * connected state to target. But USB/TA cable isn't
+                        * connected to target. The user be face with unusual
+                        * action. So, driver should check this situation in
+                        * spite of, that previous charger type is N/A.
                         */
                        break;
                case MAX77693_CHARGER_TYPE_USB:
                        /* Only USB cable, PATH:AP_USB */
-                       ret = max77693_muic_set_path(info, info->path_usb, attached);
+                       ret = max77693_muic_set_path(info, info->path_usb,
+                                                   attached);
                        if (ret < 0)
                                return ret;
 
@@ -953,7 +965,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
 
        mutex_lock(&info->mutex);
 
-       for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
+       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
                if (info->irq == muic_irqs[i].virq)
                        irq_type = muic_irqs[i].irq;
 
@@ -1171,8 +1183,9 @@ static int max77693_muic_probe(struct platform_device *pdev)
                goto err_irq;
        }
        info->edev->name = DEV_NAME;
+       info->edev->dev.parent = &pdev->dev;
        info->edev->supported_cable = max77693_extcon_cable;
-       ret = extcon_dev_register(info->edev, NULL);
+       ret = extcon_dev_register(info->edev);
        if (ret) {
                dev_err(&pdev->dev, "failed to register extcon device\n");
                goto err_irq;
@@ -1188,7 +1201,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
                num_init_data = ARRAY_SIZE(default_init_data);
        }
 
-       for (i = 0 ; i < num_init_data ; i++) {
+       for (i = 0; i < num_init_data; i++) {
                enum max77693_irq_source irq_src
                                = MAX77693_IRQ_GROUP_NR;
 
@@ -1214,7 +1227,8 @@ static int max77693_muic_probe(struct platform_device *pdev)
        }
 
        if (pdata->muic_data) {
-               struct max77693_muic_platform_data *muic_pdata = pdata->muic_data;
+               struct max77693_muic_platform_data *muic_pdata
+                                                  = pdata->muic_data;
 
                /*
                 * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB
index 67d6738d85a00d514d3d4c6692e969331663abde..6a00464658c50a6fb1aadecdf8e22467bc7963ca 100644 (file)
@@ -426,7 +426,8 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info)
                break;
        case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF:
        case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON:
-               ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, attached);
+               ret = max8997_muic_handle_usb(info,
+                                            MAX8997_USB_DEVICE, attached);
                if (ret < 0)
                        return ret;
                break;
@@ -504,7 +505,8 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info)
                }
                break;
        case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
-               extcon_set_cable_state(info->edev, "Charge-downstream", attached);
+               extcon_set_cable_state(info->edev,
+                                     "Charge-downstream", attached);
                break;
        case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
                extcon_set_cable_state(info->edev, "TA", attached);
@@ -537,7 +539,7 @@ static void max8997_muic_irq_work(struct work_struct *work)
 
        mutex_lock(&info->mutex);
 
-       for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
+       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
                if (info->irq == muic_irqs[i].virq)
                        irq_type = muic_irqs[i].irq;
 
@@ -705,8 +707,9 @@ static int max8997_muic_probe(struct platform_device *pdev)
                goto err_irq;
        }
        info->edev->name = DEV_NAME;
+       info->edev->dev.parent = &pdev->dev;
        info->edev->supported_cable = max8997_extcon_cable;
-       ret = extcon_dev_register(info->edev, NULL);
+       ret = extcon_dev_register(info->edev);
        if (ret) {
                dev_err(&pdev->dev, "failed to register extcon device\n");
                goto err_irq;
index 89fdd05c5fd61a39cb75f7c9488f261a9e04412a..6c91976dd82371d63008474cb030aa7e4cfeb507 100644 (file)
@@ -135,7 +135,7 @@ static void palmas_enable_irq(struct palmas_usb *palmas_usb)
 static int palmas_usb_probe(struct platform_device *pdev)
 {
        struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
-       struct palmas_usb_platform_data *pdata = pdev->dev.platform_data;
+       struct palmas_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct device_node *node = pdev->dev.of_node;
        struct palmas_usb *palmas_usb;
        int status;
@@ -178,9 +178,10 @@ static int palmas_usb_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, palmas_usb);
 
        palmas_usb->edev.supported_cable = palmas_extcon_cable;
+       palmas_usb->edev.dev.parent = palmas_usb->dev;
        palmas_usb->edev.mutually_exclusive = mutually_exclusive;
 
-       status = extcon_dev_register(&palmas_usb->edev, palmas_usb->dev);
+       status = extcon_dev_register(&palmas_usb->edev);
        if (status) {
                dev_err(&pdev->dev, "failed to register extcon device\n");
                return status;
index 0dee0e0c247ae5fa2f121df234d09979dc00df74..dadbac2772676c842846944c7d21771e175885e2 100644 (file)
@@ -408,7 +408,7 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
                        IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
 
        if (!value_sd) {
-               value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+               value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
                if (!value_sd) {
                        ret = -ENODEV;
                        goto err_out;
index e572dd20bdee037fed5cdce356191ea84fd41e09..fe58d0833a11c74f7b33b7ac5c169492f4a066fc 100644 (file)
@@ -61,7 +61,7 @@ static int drm_version(struct drm_device *dev, void *data,
 
 /** Ioctl table */
 static const struct drm_ioctl_desc drm_ioctls[] = {
-       DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
        DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
        DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
@@ -402,9 +402,16 @@ long drm_ioctl(struct file *filp,
                cmd = ioctl->cmd_drv;
        }
        else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) {
+               u32 drv_size;
+
                ioctl = &drm_ioctls[nr];
-               cmd = ioctl->cmd;
+
+               drv_size = _IOC_SIZE(ioctl->cmd);
                usize = asize = _IOC_SIZE(cmd);
+               if (drv_size > asize)
+                       asize = drv_size;
+
+               cmd = ioctl->cmd;
        } else
                goto err_i1;
 
index 69d8ed5416c31b2e80538fabec553e333fd741ea..2ad27880cd047bc93cf119784895040eeafc3bae 100644 (file)
@@ -505,6 +505,8 @@ static int i915_drm_freeze(struct drm_device *dev)
                intel_modeset_suspend_hw(dev);
        }
 
+       i915_gem_suspend_gtt_mappings(dev);
+
        i915_save_state(dev);
 
        intel_opregion_fini(dev);
@@ -648,7 +650,8 @@ static int i915_drm_thaw(struct drm_device *dev)
                mutex_lock(&dev->struct_mutex);
                i915_gem_restore_gtt_mappings(dev);
                mutex_unlock(&dev->struct_mutex);
-       }
+       } else if (drm_core_check_feature(dev, DRIVER_MODESET))
+               i915_check_and_clear_faults(dev);
 
        __i915_drm_thaw(dev);
 
index 35874b3a86dcc917c9bb68e1cb4879d81a0fc76b..ab0f2c0a440c6a4543a59d81282c4ab21316b2cc 100644 (file)
@@ -497,10 +497,12 @@ struct i915_address_space {
 
        /* FIXME: Need a more generic return type */
        gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr,
-                                    enum i915_cache_level level);
+                                    enum i915_cache_level level,
+                                    bool valid); /* Create a valid PTE */
        void (*clear_range)(struct i915_address_space *vm,
                            unsigned int first_entry,
-                           unsigned int num_entries);
+                           unsigned int num_entries,
+                           bool use_scratch);
        void (*insert_entries)(struct i915_address_space *vm,
                               struct sg_table *st,
                               unsigned int first_entry,
@@ -2065,6 +2067,8 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
 void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
                              struct drm_i915_gem_object *obj);
 
+void i915_check_and_clear_faults(struct drm_device *dev);
+void i915_gem_suspend_gtt_mappings(struct drm_device *dev);
 void i915_gem_restore_gtt_mappings(struct drm_device *dev);
 int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj);
 void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
index 212f6d8c35ec6593cc54957ecd24445196d26657..1f7b4caefb6e0776bf61d78af519812765b37741 100644 (file)
 #define HSW_WT_ELLC_LLC_AGE0           HSW_CACHEABILITY_CONTROL(0x6)
 
 static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level)
+                                    enum i915_cache_level level,
+                                    bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -79,9 +80,10 @@ static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
 }
 
 static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level)
+                                    enum i915_cache_level level,
+                                    bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -105,9 +107,10 @@ static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
 #define BYT_PTE_SNOOPED_BY_CPU_CACHES  (1 << 2)
 
 static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level)
+                                    enum i915_cache_level level,
+                                    bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        /* Mark the page as writeable.  Other platforms don't have a
@@ -122,9 +125,10 @@ static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
 }
 
 static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level)
+                                    enum i915_cache_level level,
+                                    bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= HSW_PTE_ADDR_ENCODE(addr);
 
        if (level != I915_CACHE_NONE)
@@ -134,9 +138,10 @@ static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
 }
 
 static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
-                                     enum i915_cache_level level)
+                                     enum i915_cache_level level,
+                                     bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= HSW_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -236,7 +241,8 @@ static int gen6_ppgtt_enable(struct drm_device *dev)
 /* PPGTT support for Sandybdrige/Gen6 and later */
 static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
                                   unsigned first_entry,
-                                  unsigned num_entries)
+                                  unsigned num_entries,
+                                  bool use_scratch)
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
@@ -245,7 +251,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
        unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
        unsigned last_pte, i;
 
-       scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC);
+       scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true);
 
        while (num_entries) {
                last_pte = first_pte + num_entries;
@@ -282,7 +288,7 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
                dma_addr_t page_addr;
 
                page_addr = sg_page_iter_dma_address(&sg_iter);
-               pt_vaddr[act_pte] = vm->pte_encode(page_addr, cache_level);
+               pt_vaddr[act_pte] = vm->pte_encode(page_addr, cache_level, true);
                if (++act_pte == I915_PPGTT_PT_ENTRIES) {
                        kunmap_atomic(pt_vaddr);
                        act_pt++;
@@ -367,7 +373,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        }
 
        ppgtt->base.clear_range(&ppgtt->base, 0,
-                               ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES);
+                               ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES, true);
 
        ppgtt->pd_offset = first_pd_entry_in_global_pt * sizeof(gen6_gtt_pte_t);
 
@@ -444,7 +450,8 @@ void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
 {
        ppgtt->base.clear_range(&ppgtt->base,
                                i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
-                               obj->base.size >> PAGE_SHIFT);
+                               obj->base.size >> PAGE_SHIFT,
+                               true);
 }
 
 extern int intel_iommu_gfx_mapped;
@@ -485,15 +492,65 @@ static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
                dev_priv->mm.interruptible = interruptible;
 }
 
+void i915_check_and_clear_faults(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
+       int i;
+
+       if (INTEL_INFO(dev)->gen < 6)
+               return;
+
+       for_each_ring(ring, dev_priv, i) {
+               u32 fault_reg;
+               fault_reg = I915_READ(RING_FAULT_REG(ring));
+               if (fault_reg & RING_FAULT_VALID) {
+                       DRM_DEBUG_DRIVER("Unexpected fault\n"
+                                        "\tAddr: 0x%08lx\\n"
+                                        "\tAddress space: %s\n"
+                                        "\tSource ID: %d\n"
+                                        "\tType: %d\n",
+                                        fault_reg & PAGE_MASK,
+                                        fault_reg & RING_FAULT_GTTSEL_MASK ? "GGTT" : "PPGTT",
+                                        RING_FAULT_SRCID(fault_reg),
+                                        RING_FAULT_FAULT_TYPE(fault_reg));
+                       I915_WRITE(RING_FAULT_REG(ring),
+                                  fault_reg & ~RING_FAULT_VALID);
+               }
+       }
+       POSTING_READ(RING_FAULT_REG(&dev_priv->ring[RCS]));
+}
+
+void i915_gem_suspend_gtt_mappings(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /* Don't bother messing with faults pre GEN6 as we have little
+        * documentation supporting that it's a good idea.
+        */
+       if (INTEL_INFO(dev)->gen < 6)
+               return;
+
+       i915_check_and_clear_faults(dev);
+
+       dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
+                                      dev_priv->gtt.base.start / PAGE_SIZE,
+                                      dev_priv->gtt.base.total / PAGE_SIZE,
+                                      false);
+}
+
 void i915_gem_restore_gtt_mappings(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj;
 
+       i915_check_and_clear_faults(dev);
+
        /* First fill our portion of the GTT with scratch pages */
        dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
                                       dev_priv->gtt.base.start / PAGE_SIZE,
-                                      dev_priv->gtt.base.total / PAGE_SIZE);
+                                      dev_priv->gtt.base.total / PAGE_SIZE,
+                                      true);
 
        list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
                i915_gem_clflush_object(obj, obj->pin_display);
@@ -536,7 +593,7 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
 
        for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
                addr = sg_page_iter_dma_address(&sg_iter);
-               iowrite32(vm->pte_encode(addr, level), &gtt_entries[i]);
+               iowrite32(vm->pte_encode(addr, level, true), &gtt_entries[i]);
                i++;
        }
 
@@ -548,7 +605,7 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
         */
        if (i != 0)
                WARN_ON(readl(&gtt_entries[i-1]) !=
-                       vm->pte_encode(addr, level));
+                       vm->pte_encode(addr, level, true));
 
        /* This next bit makes the above posting read even more important. We
         * want to flush the TLBs only after we're certain all the PTE updates
@@ -560,7 +617,8 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
 
 static void gen6_ggtt_clear_range(struct i915_address_space *vm,
                                  unsigned int first_entry,
-                                 unsigned int num_entries)
+                                 unsigned int num_entries,
+                                 bool use_scratch)
 {
        struct drm_i915_private *dev_priv = vm->dev->dev_private;
        gen6_gtt_pte_t scratch_pte, __iomem *gtt_base =
@@ -573,7 +631,8 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm,
                 first_entry, num_entries, max_entries))
                num_entries = max_entries;
 
-       scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC);
+       scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, use_scratch);
+
        for (i = 0; i < num_entries; i++)
                iowrite32(scratch_pte, &gtt_base[i]);
        readl(gtt_base);
@@ -594,7 +653,8 @@ static void i915_ggtt_insert_entries(struct i915_address_space *vm,
 
 static void i915_ggtt_clear_range(struct i915_address_space *vm,
                                  unsigned int first_entry,
-                                 unsigned int num_entries)
+                                 unsigned int num_entries,
+                                 bool unused)
 {
        intel_gtt_clear_range(first_entry, num_entries);
 }
@@ -622,7 +682,8 @@ void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
 
        dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
                                       entry,
-                                      obj->base.size >> PAGE_SHIFT);
+                                      obj->base.size >> PAGE_SHIFT,
+                                      true);
 
        obj->has_global_gtt_mapping = 0;
 }
@@ -709,11 +770,11 @@ void i915_gem_setup_global_gtt(struct drm_device *dev,
                const unsigned long count = (hole_end - hole_start) / PAGE_SIZE;
                DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n",
                              hole_start, hole_end);
-               ggtt_vm->clear_range(ggtt_vm, hole_start / PAGE_SIZE, count);
+               ggtt_vm->clear_range(ggtt_vm, hole_start / PAGE_SIZE, count, true);
        }
 
        /* And finally clear the reserved guard page */
-       ggtt_vm->clear_range(ggtt_vm, end / PAGE_SIZE - 1, 1);
+       ggtt_vm->clear_range(ggtt_vm, end / PAGE_SIZE - 1, 1, true);
 }
 
 static bool
index 38f96f65d87ad5d182cf38eff2a14b65a35067b6..ef9b35479f0136d0cb23ec7f6eb2bb59afa85e46 100644 (file)
 #define   ARB_MODE_SWIZZLE_IVB (1<<5)
 #define RENDER_HWS_PGA_GEN7    (0x04080)
 #define RING_FAULT_REG(ring)   (0x4094 + 0x100*(ring)->id)
+#define   RING_FAULT_GTTSEL_MASK (1<<11)
+#define   RING_FAULT_SRCID(x)  ((x >> 3) & 0xff)
+#define   RING_FAULT_FAULT_TYPE(x) ((x >> 1) & 0x3)
+#define   RING_FAULT_VALID     (1<<0)
 #define DONE_REG               0x40b0
 #define BSD_HWS_PGA_GEN7       (0x04180)
 #define BLT_HWS_PGA_GEN7       (0x04280)
 #define FDI_RX_CHICKEN(pipe) _PIPE(pipe, _FDI_RXA_CHICKEN, _FDI_RXB_CHICKEN)
 
 #define SOUTH_DSPCLK_GATE_D    0xc2020
+#define  PCH_DPLUNIT_CLOCK_GATE_DISABLE (1<<30)
 #define  PCH_DPLSUNIT_CLOCK_GATE_DISABLE (1<<29)
+#define  PCH_CPUNIT_CLOCK_GATE_DISABLE (1<<14)
 #define  PCH_LP_PARTITION_LEVEL_DISABLE  (1<<12)
 
 /* CPU: FDI_TX */
index ea9022ef15d5bdffc40e5c0bf9a941c7f0b38677..10d1de5bce6ff7a35921fa19d1327863b85ab86d 100644 (file)
@@ -83,8 +83,7 @@ static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_crt_get_config(struct intel_encoder *encoder,
-                                struct intel_crtc_config *pipe_config)
+static unsigned int intel_crt_get_flags(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        struct intel_crt *crt = intel_encoder_to_crt(encoder);
@@ -102,7 +101,25 @@ static void intel_crt_get_config(struct intel_encoder *encoder,
        else
                flags |= DRM_MODE_FLAG_NVSYNC;
 
-       pipe_config->adjusted_mode.flags |= flags;
+       return flags;
+}
+
+static void intel_crt_get_config(struct intel_encoder *encoder,
+                                struct intel_crtc_config *pipe_config)
+{
+       pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder);
+}
+
+static void hsw_crt_get_config(struct intel_encoder *encoder,
+                              struct intel_crtc_config *pipe_config)
+{
+       intel_ddi_get_config(encoder, pipe_config);
+
+       pipe_config->adjusted_mode.flags &= ~(DRM_MODE_FLAG_PHSYNC |
+                                             DRM_MODE_FLAG_NHSYNC |
+                                             DRM_MODE_FLAG_PVSYNC |
+                                             DRM_MODE_FLAG_NVSYNC);
+       pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder);
 }
 
 /* Note: The caller is required to filter out dpms modes not supported by the
@@ -799,7 +816,10 @@ void intel_crt_init(struct drm_device *dev)
        crt->base.mode_set = intel_crt_mode_set;
        crt->base.disable = intel_disable_crt;
        crt->base.enable = intel_enable_crt;
-       crt->base.get_config = intel_crt_get_config;
+       if (IS_HASWELL(dev))
+               crt->base.get_config = hsw_crt_get_config;
+       else
+               crt->base.get_config = intel_crt_get_config;
        if (I915_HAS_HOTPLUG(dev))
                crt->base.hpd_pin = HPD_CRT;
        if (HAS_DDI(dev))
index 63de2701b97403a82ffd221424d5b5b9acda5843..b53fff84a7d5b5521e1a34c6749d1f91a5af94e4 100644 (file)
@@ -1249,8 +1249,8 @@ static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
                intel_dp_check_link_status(intel_dp);
 }
 
-static void intel_ddi_get_config(struct intel_encoder *encoder,
-                                struct intel_crtc_config *pipe_config)
+void intel_ddi_get_config(struct intel_encoder *encoder,
+                         struct intel_crtc_config *pipe_config)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
@@ -1268,6 +1268,23 @@ static void intel_ddi_get_config(struct intel_encoder *encoder,
                flags |= DRM_MODE_FLAG_NVSYNC;
 
        pipe_config->adjusted_mode.flags |= flags;
+
+       switch (temp & TRANS_DDI_BPC_MASK) {
+       case TRANS_DDI_BPC_6:
+               pipe_config->pipe_bpp = 18;
+               break;
+       case TRANS_DDI_BPC_8:
+               pipe_config->pipe_bpp = 24;
+               break;
+       case TRANS_DDI_BPC_10:
+               pipe_config->pipe_bpp = 30;
+               break;
+       case TRANS_DDI_BPC_12:
+               pipe_config->pipe_bpp = 36;
+               break;
+       default:
+               break;
+       }
 }
 
 static void intel_ddi_destroy(struct drm_encoder *encoder)
index 581fb4b2f76637694877a2c4401acd2f35ade58c..d78d33f9337d993472b22f82f3e2749d28a3e38e 100644 (file)
@@ -2327,9 +2327,10 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc)
                           FDI_FE_ERRC_ENABLE);
 }
 
-static bool pipe_has_enabled_pch(struct intel_crtc *intel_crtc)
+static bool pipe_has_enabled_pch(struct intel_crtc *crtc)
 {
-       return intel_crtc->base.enabled && intel_crtc->config.has_pch_encoder;
+       return crtc->base.enabled && crtc->active &&
+               crtc->config.has_pch_encoder;
 }
 
 static void ivb_modeset_global_resources(struct drm_device *dev)
@@ -2979,6 +2980,48 @@ static void ironlake_pch_transcoder_set_timings(struct intel_crtc *crtc,
                   I915_READ(VSYNCSHIFT(cpu_transcoder)));
 }
 
+static void cpt_enable_fdi_bc_bifurcation(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t temp;
+
+       temp = I915_READ(SOUTH_CHICKEN1);
+       if (temp & FDI_BC_BIFURCATION_SELECT)
+               return;
+
+       WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE);
+       WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE);
+
+       temp |= FDI_BC_BIFURCATION_SELECT;
+       DRM_DEBUG_KMS("enabling fdi C rx\n");
+       I915_WRITE(SOUTH_CHICKEN1, temp);
+       POSTING_READ(SOUTH_CHICKEN1);
+}
+
+static void ivybridge_update_fdi_bc_bifurcation(struct intel_crtc *intel_crtc)
+{
+       struct drm_device *dev = intel_crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       switch (intel_crtc->pipe) {
+       case PIPE_A:
+               break;
+       case PIPE_B:
+               if (intel_crtc->config.fdi_lanes > 2)
+                       WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT);
+               else
+                       cpt_enable_fdi_bc_bifurcation(dev);
+
+               break;
+       case PIPE_C:
+               cpt_enable_fdi_bc_bifurcation(dev);
+
+               break;
+       default:
+               BUG();
+       }
+}
+
 /*
  * Enable PCH resources required for PCH ports:
  *   - PCH PLLs
@@ -2997,6 +3040,9 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
 
        assert_pch_transcoder_disabled(dev_priv, pipe);
 
+       if (IS_IVYBRIDGE(dev))
+               ivybridge_update_fdi_bc_bifurcation(intel_crtc);
+
        /* Write the TU size bits before fdi link training, so that error
         * detection works. */
        I915_WRITE(FDI_RX_TUSIZE1(pipe),
@@ -4983,6 +5029,22 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
        if (!(tmp & PIPECONF_ENABLE))
                return false;
 
+       if (IS_G4X(dev) || IS_VALLEYVIEW(dev)) {
+               switch (tmp & PIPECONF_BPC_MASK) {
+               case PIPECONF_6BPC:
+                       pipe_config->pipe_bpp = 18;
+                       break;
+               case PIPECONF_8BPC:
+                       pipe_config->pipe_bpp = 24;
+                       break;
+               case PIPECONF_10BPC:
+                       pipe_config->pipe_bpp = 30;
+                       break;
+               default:
+                       break;
+               }
+       }
+
        intel_get_pipe_timings(crtc, pipe_config);
 
        i9xx_get_pfit_config(crtc, pipe_config);
@@ -5576,48 +5638,6 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc,
        return true;
 }
 
-static void cpt_enable_fdi_bc_bifurcation(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t temp;
-
-       temp = I915_READ(SOUTH_CHICKEN1);
-       if (temp & FDI_BC_BIFURCATION_SELECT)
-               return;
-
-       WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE);
-       WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE);
-
-       temp |= FDI_BC_BIFURCATION_SELECT;
-       DRM_DEBUG_KMS("enabling fdi C rx\n");
-       I915_WRITE(SOUTH_CHICKEN1, temp);
-       POSTING_READ(SOUTH_CHICKEN1);
-}
-
-static void ivybridge_update_fdi_bc_bifurcation(struct intel_crtc *intel_crtc)
-{
-       struct drm_device *dev = intel_crtc->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       switch (intel_crtc->pipe) {
-       case PIPE_A:
-               break;
-       case PIPE_B:
-               if (intel_crtc->config.fdi_lanes > 2)
-                       WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT);
-               else
-                       cpt_enable_fdi_bc_bifurcation(dev);
-
-               break;
-       case PIPE_C:
-               cpt_enable_fdi_bc_bifurcation(dev);
-
-               break;
-       default:
-               BUG();
-       }
-}
-
 int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp)
 {
        /*
@@ -5811,9 +5831,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                                             &intel_crtc->config.fdi_m_n);
        }
 
-       if (IS_IVYBRIDGE(dev))
-               ivybridge_update_fdi_bc_bifurcation(intel_crtc);
-
        ironlake_set_pipeconf(crtc);
 
        /* Set up the display plane register */
@@ -5881,6 +5898,23 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
        if (!(tmp & PIPECONF_ENABLE))
                return false;
 
+       switch (tmp & PIPECONF_BPC_MASK) {
+       case PIPECONF_6BPC:
+               pipe_config->pipe_bpp = 18;
+               break;
+       case PIPECONF_8BPC:
+               pipe_config->pipe_bpp = 24;
+               break;
+       case PIPECONF_10BPC:
+               pipe_config->pipe_bpp = 30;
+               break;
+       case PIPECONF_12BPC:
+               pipe_config->pipe_bpp = 36;
+               break;
+       default:
+               break;
+       }
+
        if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) {
                struct intel_shared_dpll *pll;
 
@@ -8612,6 +8646,9 @@ intel_pipe_config_compare(struct drm_device *dev,
        PIPE_CONF_CHECK_X(dpll_hw_state.fp0);
        PIPE_CONF_CHECK_X(dpll_hw_state.fp1);
 
+       if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5)
+               PIPE_CONF_CHECK_I(pipe_bpp);
+
 #undef PIPE_CONF_CHECK_X
 #undef PIPE_CONF_CHECK_I
 #undef PIPE_CONF_CHECK_FLAGS
index 2c555f91bfae076688fe07e1694c75932a9c97d2..1a431377d83b76ad22bccf795db770ab3b40bd3e 100644 (file)
@@ -1401,6 +1401,26 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
                else
                        pipe_config->port_clock = 270000;
        }
+
+       if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp &&
+           pipe_config->pipe_bpp > dev_priv->vbt.edp_bpp) {
+               /*
+                * This is a big fat ugly hack.
+                *
+                * Some machines in UEFI boot mode provide us a VBT that has 18
+                * bpp and 1.62 GHz link bandwidth for eDP, which for reasons
+                * unknown we fail to light up. Yet the same BIOS boots up with
+                * 24 bpp and 2.7 GHz link. Use the same bpp as the BIOS uses as
+                * max, not what it tells us to use.
+                *
+                * Note: This will still be broken if the eDP panel is not lit
+                * up by the BIOS, and thus we can't get the mode at module
+                * load.
+                */
+               DRM_DEBUG_KMS("pipe has %d bpp for eDP panel, overriding BIOS-provided max %d bpp\n",
+                             pipe_config->pipe_bpp, dev_priv->vbt.edp_bpp);
+               dev_priv->vbt.edp_bpp = pipe_config->pipe_bpp;
+       }
 }
 
 static bool is_edp_psr(struct intel_dp *intel_dp)
index 9b7b68fd5d47cf6c979c8fdffd412d8381aabc0e..7f2b384ac939834fe8e024df14a3e47e4e82ad18 100644 (file)
@@ -765,6 +765,8 @@ extern void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
 extern bool
 intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
 extern void intel_ddi_fdi_disable(struct drm_crtc *crtc);
+extern void intel_ddi_get_config(struct intel_encoder *encoder,
+                                struct intel_crtc_config *pipe_config);
 
 extern void intel_display_handle_reset(struct drm_device *dev);
 extern bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
index 831a5c021c4bdefd2495ca0736d8fcb3f78ff57d..b8af94a5be390610b360b5b27c3fe6e01d002081 100644 (file)
@@ -698,6 +698,22 @@ static const struct dmi_system_id intel_no_lvds[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"),
                },
        },
+       {
+               .callback = intel_no_lvds_dmi_callback,
+               .ident = "Intel D410PT",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
+                       DMI_MATCH(DMI_BOARD_NAME, "D410PT"),
+               },
+       },
+       {
+               .callback = intel_no_lvds_dmi_callback,
+               .ident = "Intel D425KT",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "D425KT"),
+               },
+       },
        {
                .callback = intel_no_lvds_dmi_callback,
                .ident = "Intel D510MO",
index f4c5e95b2d6f633b8151832973f6b831ab0bc559..26c2ea3e985c22d7011e68f3e73d14fa8e896131 100644 (file)
@@ -4759,7 +4759,9 @@ static void cpt_init_clock_gating(struct drm_device *dev)
         * gating for the panel power sequencer or it will fail to
         * start up when no ports are active.
         */
-       I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
+       I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE |
+                  PCH_DPLUNIT_CLOCK_GATE_DISABLE |
+                  PCH_CPUNIT_CLOCK_GATE_DISABLE);
        I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) |
                   DPLS_EDP_PPS_FIX_DIS);
        /* The below fixes the weird display corruption, a few pixels shifted
index 32923d2f60021105a5092826bcd7810ca054c960..5e891b226acf9cf60db309fc8b8c596660fe1d96 100644 (file)
@@ -707,24 +707,37 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        switch (connector->connector_type) {
        case DRM_MODE_CONNECTOR_DVII:
        case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */
-               if ((radeon_connector->audio == RADEON_AUDIO_ENABLE) ||
-                   (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                    (radeon_connector->audio == RADEON_AUDIO_AUTO)))
-                       return ATOM_ENCODER_MODE_HDMI;
-               else if (radeon_connector->use_digital)
+               if (radeon_audio != 0) {
+                       if (radeon_connector->use_digital &&
+                           (radeon_connector->audio == RADEON_AUDIO_ENABLE))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (radeon_connector->use_digital)
+                               return ATOM_ENCODER_MODE_DVI;
+                       else
+                               return ATOM_ENCODER_MODE_CRT;
+               } else if (radeon_connector->use_digital) {
                        return ATOM_ENCODER_MODE_DVI;
-               else
+               } else {
                        return ATOM_ENCODER_MODE_CRT;
+               }
                break;
        case DRM_MODE_CONNECTOR_DVID:
        case DRM_MODE_CONNECTOR_HDMIA:
        default:
-               if ((radeon_connector->audio == RADEON_AUDIO_ENABLE) ||
-                   (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                    (radeon_connector->audio == RADEON_AUDIO_AUTO)))
-                       return ATOM_ENCODER_MODE_HDMI;
-               else
+               if (radeon_audio != 0) {
+                       if (radeon_connector->audio == RADEON_AUDIO_ENABLE)
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else
+                               return ATOM_ENCODER_MODE_DVI;
+               } else {
                        return ATOM_ENCODER_MODE_DVI;
+               }
                break;
        case DRM_MODE_CONNECTOR_LVDS:
                return ATOM_ENCODER_MODE_LVDS;
@@ -732,14 +745,19 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        case DRM_MODE_CONNECTOR_DisplayPort:
                dig_connector = radeon_connector->con_priv;
                if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
-                   (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
+                   (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
                        return ATOM_ENCODER_MODE_DP;
-               else if ((radeon_connector->audio == RADEON_AUDIO_ENABLE) ||
-                        (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                         (radeon_connector->audio == RADEON_AUDIO_AUTO)))
-                       return ATOM_ENCODER_MODE_HDMI;
-               else
+               } else if (radeon_audio != 0) {
+                       if (radeon_connector->audio == RADEON_AUDIO_ENABLE)
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else
+                               return ATOM_ENCODER_MODE_DVI;
+               } else {
                        return ATOM_ENCODER_MODE_DVI;
+               }
                break;
        case DRM_MODE_CONNECTOR_eDP:
                return ATOM_ENCODER_MODE_DP;
@@ -1655,7 +1673,7 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
                         * does the same thing and more.
                         */
                        if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730) &&
-                           (rdev->family != CHIP_RS880))
+                           (rdev->family != CHIP_RS780) && (rdev->family != CHIP_RS880))
                                atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
                }
                if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
index b874ccdf52f7c2807461fef654290bc09da7c55d..9cd2bc989ac713d1604cec5ab1311ac3680066c5 100644 (file)
@@ -1694,6 +1694,7 @@ static int cik_init_microcode(struct radeon_device *rdev)
                               fw_name);
                        release_firmware(rdev->smc_fw);
                        rdev->smc_fw = NULL;
+                       err = 0;
                } else if (rdev->smc_fw->size != smc_req_size) {
                        printk(KERN_ERR
                               "cik_smc: Bogus length %zu in firmware \"%s\"\n",
@@ -3182,6 +3183,7 @@ int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
        r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
        if (r) {
                DRM_ERROR("radeon: failed to get ib (%d).\n", r);
+               radeon_scratch_free(rdev, scratch);
                return r;
        }
        ib.ptr[0] = PACKET3(PACKET3_SET_UCONFIG_REG, 1);
@@ -3198,6 +3200,8 @@ int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
        r = radeon_fence_wait(ib.fence, false);
        if (r) {
                DRM_ERROR("radeon: fence wait failed (%d).\n", r);
+               radeon_scratch_free(rdev, scratch);
+               radeon_ib_free(rdev, &ib);
                return r;
        }
        for (i = 0; i < rdev->usec_timeout; i++) {
index 85a69d2ea3d2c8d86c9c552902d260dd576ac927..9fcd338c0fcf6bb1e113c80c8b116bcf5c56abbc 100644 (file)
@@ -113,6 +113,9 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        u8 *sadb;
        int sad_count;
 
+       /* XXX: setting this register causes hangs on some asics */
+       return;
+
        if (!dig->afmt->pin)
                return;
 
index f815c20640bd45e28c9d27fa86897a99af5d40d6..57fcc4b16a526d166fd6be21955a86d5ee7d87dc 100644 (file)
@@ -67,6 +67,9 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        u8 *sadb;
        int sad_count;
 
+       /* XXX: setting this register causes hangs on some asics */
+       return;
+
        list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
                if (connector->encoder == encoder)
                        radeon_connector = to_radeon_connector(connector);
@@ -288,6 +291,7 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
        /* fglrx clears sth in AFMT_AUDIO_PACKET_CONTROL2 here */
 
        WREG32(HDMI_ACR_PACKET_CONTROL + offset,
+              HDMI_ACR_SOURCE | /* select SW CTS value */
               HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
 
        evergreen_hdmi_update_ACR(encoder, mode->clock);
index 71399065db04d309104b98ede05b69226ad3b271..b41905573cd2a431862b3684f4d43ad445ecd15d 100644 (file)
@@ -2635,7 +2635,7 @@ int kv_dpm_init(struct radeon_device *rdev)
        pi->caps_sclk_ds = true;
        pi->enable_auto_thermal_throttling = true;
        pi->disable_nb_ps3_in_battery = false;
-       pi->bapm_enable = true;
+       pi->bapm_enable = false;
        pi->voltage_drop_t = 0;
        pi->caps_sclk_throttle_low_notification = false;
        pi->caps_fps = false; /* true? */
index 93c1f9ef5da9b5ee7c3474bfaaae5bbe7497cf91..cac2866d79da441dfbbbcef860f713c9c7ad5a58 100644 (file)
@@ -804,6 +804,7 @@ int ni_init_microcode(struct radeon_device *rdev)
                               fw_name);
                        release_firmware(rdev->smc_fw);
                        rdev->smc_fw = NULL;
+                       err = 0;
                } else if (rdev->smc_fw->size != smc_req_size) {
                        printk(KERN_ERR
                               "ni_mc: Bogus length %zu in firmware \"%s\"\n",
index 2a1b1876b4312eb332c017cb027aeb346bf2c850..f9be22062df1eb8048384cabec6219e8a5238fff 100644 (file)
@@ -2302,6 +2302,7 @@ int r600_init_microcode(struct radeon_device *rdev)
                               fw_name);
                        release_firmware(rdev->smc_fw);
                        rdev->smc_fw = NULL;
+                       err = 0;
                } else if (rdev->smc_fw->size != smc_req_size) {
                        printk(KERN_ERR
                               "smc: Bogus length %zu in firmware \"%s\"\n",
index 5b729319f27b880dd70c52e19d5aeb33fe1d2cd8..06022e3b9c3bdc0a0660a60c448e25038659e41d 100644 (file)
@@ -309,6 +309,9 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        u8 *sadb;
        int sad_count;
 
+       /* XXX: setting this register causes hangs on some asics */
+       return;
+
        list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
                if (connector->encoder == encoder)
                        radeon_connector = to_radeon_connector(connector);
index a400ac1c414715423064874d37f047b426f7fe19..24f4960f59ee57be931f3f5301528a859469a016 100644 (file)
@@ -1272,8 +1272,8 @@ struct radeon_blacklist_clocks
 struct radeon_clock_and_voltage_limits {
        u32 sclk;
        u32 mclk;
-       u32 vddc;
-       u32 vddci;
+       u16 vddc;
+       u16 vddci;
 };
 
 struct radeon_clock_array {
index 79159b5da05bc778dc71b819a32c4139d28f967b..64565732cb98cf25af4e4d1f564e307c85cf4a8f 100644 (file)
@@ -1658,9 +1658,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                        drm_object_attach_property(&radeon_connector->base.base,
                                                      rdev->mode_info.underscan_vborder_property,
                                                      0);
-                       drm_object_attach_property(&radeon_connector->base.base,
-                                                  rdev->mode_info.audio_property,
-                                                  RADEON_AUDIO_DISABLE);
+                       if (radeon_audio != 0)
+                               drm_object_attach_property(&radeon_connector->base.base,
+                                                          rdev->mode_info.audio_property,
+                                                          (radeon_audio == 1) ?
+                                                          RADEON_AUDIO_AUTO :
+                                                          RADEON_AUDIO_DISABLE);
                        subpixel_order = SubPixelHorizontalRGB;
                        connector->interlace_allowed = true;
                        if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
@@ -1754,10 +1757,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                                                              rdev->mode_info.underscan_vborder_property,
                                                              0);
                        }
-                       if (ASIC_IS_DCE2(rdev)) {
+                       if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
-                                                             rdev->mode_info.audio_property,
-                                                             RADEON_AUDIO_DISABLE);
+                                                          rdev->mode_info.audio_property,
+                                                          (radeon_audio == 1) ?
+                                                          RADEON_AUDIO_AUTO :
+                                                          RADEON_AUDIO_DISABLE);
                        }
                        if (connector_type == DRM_MODE_CONNECTOR_DVII) {
                                radeon_connector->dac_load_detect = true;
@@ -1799,10 +1804,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                                                              rdev->mode_info.underscan_vborder_property,
                                                              0);
                        }
-                       if (ASIC_IS_DCE2(rdev)) {
+                       if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
-                                                             rdev->mode_info.audio_property,
-                                                             RADEON_AUDIO_DISABLE);
+                                                          rdev->mode_info.audio_property,
+                                                          (radeon_audio == 1) ?
+                                                          RADEON_AUDIO_AUTO :
+                                                          RADEON_AUDIO_DISABLE);
                        }
                        subpixel_order = SubPixelHorizontalRGB;
                        connector->interlace_allowed = true;
@@ -1843,10 +1850,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                                                              rdev->mode_info.underscan_vborder_property,
                                                              0);
                        }
-                       if (ASIC_IS_DCE2(rdev)) {
+                       if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
-                                                             rdev->mode_info.audio_property,
-                                                             RADEON_AUDIO_DISABLE);
+                                                          rdev->mode_info.audio_property,
+                                                          (radeon_audio == 1) ?
+                                                          RADEON_AUDIO_AUTO :
+                                                          RADEON_AUDIO_DISABLE);
                        }
                        connector->interlace_allowed = true;
                        /* in theory with a DP to VGA converter... */
index 66c222836631a4bc8dcd12b2f81f6e935218c566..80285e35bc6513fda3d5f5c975399a60feaab1e3 100644 (file)
@@ -85,9 +85,8 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
                   VRAM, also but everything into VRAM on AGP cards to avoid
                   image corruptions */
                if (p->ring == R600_RING_TYPE_UVD_INDEX &&
-                   p->rdev->family < CHIP_PALM &&
                    (i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) {
-
+                       /* TODO: is this still needed for NI+ ? */
                        p->relocs[i].lobj.domain =
                                RADEON_GEM_DOMAIN_VRAM;
 
index cdd12dcd988b1ed3260076b6d984cbf388dc35a7..9c14a1ba1de43aa9086741fda75c9aba3cc96394 100644 (file)
@@ -153,7 +153,7 @@ int radeon_benchmarking = 0;
 int radeon_testing = 0;
 int radeon_connector_table = 0;
 int radeon_tv = 1;
-int radeon_audio = 1;
+int radeon_audio = -1;
 int radeon_disp_priority = 0;
 int radeon_hw_i2c = 0;
 int radeon_pcie_gen2 = -1;
@@ -196,7 +196,7 @@ module_param_named(connector_table, radeon_connector_table, int, 0444);
 MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
 module_param_named(tv, radeon_tv, int, 0444);
 
-MODULE_PARM_DESC(audio, "Audio enable (1 = enable)");
+MODULE_PARM_DESC(audio, "Audio enable (-1 = auto, 0 = disable, 1 = enable)");
 module_param_named(audio, radeon_audio, int, 0444);
 
 MODULE_PARM_DESC(disp_priority, "Display Priority (0 = auto, 1 = normal, 2 = high)");
index 4f2e73f79638f244c43558722128910404b2b04b..308eff5be1b420b74b18ccb1b1e0c89f4bd67f32 100644 (file)
@@ -476,7 +476,8 @@ static int radeon_uvd_cs_reloc(struct radeon_cs_parser *p,
                return -EINVAL;
        }
 
-       if (p->rdev->family < CHIP_PALM && (cmd == 0 || cmd == 0x3) &&
+       /* TODO: is this still necessary on NI+ ? */
+       if ((cmd == 0 || cmd == 0x3) &&
            (start >> 28) != (p->rdev->uvd.gpu_addr >> 28)) {
                DRM_ERROR("msg/fb buffer %LX-%LX out of 256MB segment!\n",
                          start, end);
index d4652af425b87b11c6b723930473b3b810742b10..d96f7cbca0a115f58eaeca9df3a6e585d6b3445d 100644 (file)
@@ -1681,6 +1681,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                       fw_name);
                release_firmware(rdev->smc_fw);
                rdev->smc_fw = NULL;
+               err = 0;
        } else if (rdev->smc_fw->size != smc_req_size) {
                printk(KERN_ERR
                       "si_smc: Bogus length %zu in firmware \"%s\"\n",
index 3100fa9cb52f4ff5f0b924af6d146f1e06ae6908..7266805d9786c6fe9bfd38501f7ab674f7d05a73 100644 (file)
@@ -212,8 +212,8 @@ int uvd_v1_0_start(struct radeon_device *rdev)
        /* enable VCPU clock */
        WREG32(UVD_VCPU_CNTL,  1 << 9);
 
-       /* enable UMC and NC0 */
-       WREG32_P(UVD_LMI_CTRL2, 1 << 13, ~((1 << 8) | (1 << 13)));
+       /* enable UMC */
+       WREG32_P(UVD_LMI_CTRL2, 0, ~(1 << 8));
 
        /* boot up the VCPU */
        WREG32(UVD_SOFT_RESET, 0);
index 1a90f0a2f7e5aa7b994558b18a3cffd2d1254ee8..0508f93b9795fbc2b4ccf9c8f34ea62383a7e138 100644 (file)
@@ -740,9 +740,17 @@ static void vmw_postclose(struct drm_device *dev,
        struct vmw_fpriv *vmw_fp;
 
        vmw_fp = vmw_fpriv(file_priv);
-       ttm_object_file_release(&vmw_fp->tfile);
-       if (vmw_fp->locked_master)
+
+       if (vmw_fp->locked_master) {
+               struct vmw_master *vmaster =
+                       vmw_master(vmw_fp->locked_master);
+
+               ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
+               ttm_vt_unlock(&vmaster->lock);
                drm_master_put(&vmw_fp->locked_master);
+       }
+
+       ttm_object_file_release(&vmw_fp->tfile);
        kfree(vmw_fp);
 }
 
@@ -925,14 +933,13 @@ static void vmw_master_drop(struct drm_device *dev,
 
        vmw_fp->locked_master = drm_master_get(file_priv->master);
        ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
-       vmw_execbuf_release_pinned_bo(dev_priv);
-
        if (unlikely((ret != 0))) {
                DRM_ERROR("Unable to lock TTM at VT switch.\n");
                drm_master_put(&vmw_fp->locked_master);
        }
 
-       ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
+       ttm_lock_set_kill(&vmaster->lock, false, SIGTERM);
+       vmw_execbuf_release_pinned_bo(dev_priv);
 
        if (!dev_priv->enable_fb) {
                ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
index 0e67cf41065d801e6526d23dc5cdf375bdabe1c7..37fb4befec82634ccf3a504428af8ed60f4a0e56 100644 (file)
@@ -970,7 +970,7 @@ void vmw_resource_unreserve(struct vmw_resource *res,
        if (new_backup)
                res->backup_offset = new_backup_offset;
 
-       if (!res->func->may_evict)
+       if (!res->func->may_evict || res->id == -1)
                return;
 
        write_lock(&dev_priv->resource_lock);
index 5a8c01112a233a5ce52ce7260d19376720c74042..e80da62363bc2c96d6be419f89d002f3a55e4d36 100644 (file)
@@ -319,7 +319,7 @@ static s32 item_sdata(struct hid_item *item)
 
 static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 {
-       __u32 raw_value;
+       __s32 raw_value;
        switch (item->tag) {
        case HID_GLOBAL_ITEM_TAG_PUSH:
 
@@ -370,10 +370,11 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
                return 0;
 
        case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
-               /* Units exponent negative numbers are given through a
-                * two's complement.
-                * See "6.2.2.7 Global Items" for more information. */
-               raw_value = item_udata(item);
+               /* Many devices provide unit exponent as a two's complement
+                * nibble due to the common misunderstanding of HID
+                * specification 1.11, 6.2.2.7 Global Items. Attempt to handle
+                * both this and the standard encoding. */
+               raw_value = item_sdata(item);
                if (!(raw_value & 0xfffffff0))
                        parser->global.unit_exponent = hid_snto32(raw_value, 4);
                else
@@ -1870,6 +1871,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO2, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
        { }
 };
index 9cbc7ab07dfaee581141b0191f4e76300a18d1d6..f0296a50be5f754fc7be2b8f593bf2b2beca1e22 100644 (file)
 #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN   0x0003
 
 #define USB_VENDOR_ID_NINTENDO         0x057e
+#define USB_VENDOR_ID_NINTENDO2                0x054c
 #define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
 #define USB_DEVICE_ID_NINTENDO_WIIMOTE2        0x0330
 
 #define USB_DEVICE_ID_SYNAPTICS_COMP_TP        0x0009
 #define USB_DEVICE_ID_SYNAPTICS_WTP    0x0010
 #define USB_DEVICE_ID_SYNAPTICS_DPAD   0x0013
+#define USB_DEVICE_ID_SYNAPTICS_LTS1   0x0af8
+#define USB_DEVICE_ID_SYNAPTICS_LTS2   0x1d10
 
 #define USB_VENDOR_ID_THINGM           0x27b8
 #define USB_DEVICE_ID_BLINK1           0x01ed
 #define USB_VENDOR_ID_PRIMAX   0x0461
 #define USB_DEVICE_ID_PRIMAX_KEYBOARD  0x4e05
 
+#define USB_VENDOR_ID_SIS      0x0457
+#define USB_DEVICE_ID_SIS_TS   0x1013
+
 #endif
index 8741d953dcc80acb552bac187ffe6997ad95ca4e..d97f2323af573ecf229f9d76b536634c57c26e17 100644 (file)
@@ -192,6 +192,7 @@ static int hidinput_setkeycode(struct input_dev *dev,
        return -EINVAL;
 }
 
+
 /**
  * hidinput_calc_abs_res - calculate an absolute axis resolution
  * @field: the HID report field to calculate resolution for
@@ -234,23 +235,17 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
        case ABS_MT_TOOL_Y:
        case ABS_MT_TOUCH_MAJOR:
        case ABS_MT_TOUCH_MINOR:
-               if (field->unit & 0xffffff00)           /* Not a length */
-                       return 0;
-               unit_exponent += hid_snto32(field->unit >> 4, 4) - 1;
-               switch (field->unit & 0xf) {
-               case 0x1:                               /* If centimeters */
+               if (field->unit == 0x11) {              /* If centimeters */
                        /* Convert to millimeters */
                        unit_exponent += 1;
-                       break;
-               case 0x3:                               /* If inches */
+               } else if (field->unit == 0x13) {       /* If inches */
                        /* Convert to millimeters */
                        prev = physical_extents;
                        physical_extents *= 254;
                        if (physical_extents < prev)
                                return 0;
                        unit_exponent -= 1;
-                       break;
-               default:
+               } else {
                        return 0;
                }
                break;
index abb20db2b443ccdcc34159a97fcc83307db65c40..1446f526ee8bbade2290b615fc14b6aae35dc09f 100644 (file)
@@ -834,7 +834,8 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
                goto done;
        }
 
-       if (vendor == USB_VENDOR_ID_NINTENDO) {
+       if (vendor == USB_VENDOR_ID_NINTENDO ||
+           vendor == USB_VENDOR_ID_NINTENDO2) {
                if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) {
                        devtype = WIIMOTE_DEV_GEN10;
                        goto done;
@@ -1855,6 +1856,8 @@ static void wiimote_hid_remove(struct hid_device *hdev)
 static const struct hid_device_id wiimote_hid_devices[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
                                USB_DEVICE_ID_NINTENDO_WIIMOTE) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO2,
+                               USB_DEVICE_ID_NINTENDO_WIIMOTE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
                                USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
        { }
index 07345521f4210f4988627cff4421beedc96d1c06..3fca3be08337d76fdd8ecaec09c3d1693b0ac4ef 100644 (file)
@@ -110,6 +110,9 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SIS, USB_DEVICE_ID_SIS_TS, HID_QUIRK_NO_INIT_REPORTS },
 
        { 0, 0 }
 };
index 66d44581e1b1e74a242a8b8cdbf28ac3196325c3..749f7b5c81795e6244c2cdc14abc7683919032c3 100644 (file)
@@ -33,11 +33,13 @@ static ssize_t modalias_show(struct device *dev,
 {
        return sprintf(buf, "hsi:%s\n", dev_name(dev));
 }
+static DEVICE_ATTR_RO(modalias);
 
-static struct device_attribute hsi_bus_dev_attrs[] = {
-       __ATTR_RO(modalias),
-       __ATTR_NULL,
+static struct attribute *hsi_bus_dev_attrs[] = {
+       &dev_attr_modalias.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(hsi_bus_dev);
 
 static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
@@ -53,7 +55,7 @@ static int hsi_bus_match(struct device *dev, struct device_driver *driver)
 
 static struct bus_type hsi_bus_type = {
        .name           = "hsi",
-       .dev_attrs      = hsi_bus_dev_attrs,
+       .dev_groups     = hsi_bus_dev_groups,
        .match          = hsi_bus_match,
        .uevent         = hsi_bus_uevent,
 };
index 6de6c98ce6eb73636d06123024e9f84324947f59..cea623c36ae23cc007138371b14ef05e770fe6d0 100644 (file)
@@ -47,8 +47,8 @@ static void vmbus_setevent(struct vmbus_channel *channel)
                        (unsigned long *) vmbus_connection.send_int_page +
                        (channel->offermsg.child_relid >> 5));
 
-               monitorpage = vmbus_connection.monitor_pages;
-               monitorpage++; /* Get the child to parent monitor page */
+               /* Get the child to parent monitor page */
+               monitorpage = vmbus_connection.monitor_pages[1];
 
                sync_set_bit(channel->monitor_bit,
                        (unsigned long *)&monitorpage->trigger_group
@@ -59,50 +59,6 @@ static void vmbus_setevent(struct vmbus_channel *channel)
        }
 }
 
-/*
- * vmbus_get_debug_info -Retrieve various channel debug info
- */
-void vmbus_get_debug_info(struct vmbus_channel *channel,
-                             struct vmbus_channel_debug_info *debuginfo)
-{
-       struct hv_monitor_page *monitorpage;
-       u8 monitor_group = (u8)channel->offermsg.monitorid / 32;
-       u8 monitor_offset = (u8)channel->offermsg.monitorid % 32;
-
-       debuginfo->relid = channel->offermsg.child_relid;
-       debuginfo->state = channel->state;
-       memcpy(&debuginfo->interfacetype,
-              &channel->offermsg.offer.if_type, sizeof(uuid_le));
-       memcpy(&debuginfo->interface_instance,
-              &channel->offermsg.offer.if_instance,
-              sizeof(uuid_le));
-
-       monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages;
-
-       debuginfo->monitorid = channel->offermsg.monitorid;
-
-       debuginfo->servermonitor_pending =
-                       monitorpage->trigger_group[monitor_group].pending;
-       debuginfo->servermonitor_latency =
-                       monitorpage->latency[monitor_group][monitor_offset];
-       debuginfo->servermonitor_connectionid =
-                       monitorpage->parameter[monitor_group]
-                                       [monitor_offset].connectionid.u.id;
-
-       monitorpage++;
-
-       debuginfo->clientmonitor_pending =
-                       monitorpage->trigger_group[monitor_group].pending;
-       debuginfo->clientmonitor_latency =
-                       monitorpage->latency[monitor_group][monitor_offset];
-       debuginfo->clientmonitor_connectionid =
-                       monitorpage->parameter[monitor_group]
-                                       [monitor_offset].connectionid.u.id;
-
-       hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound);
-       hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound);
-}
-
 /*
  * vmbus_open - Open the specified channel.
  */
@@ -855,6 +811,6 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer,
        if (signal)
                vmbus_setevent(channel);
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw);
index bbff5f200bef7a7b070abec6c0f7c202f335159c..fa920469bf104fda2e57d9c2bce2f15f90d5b2a4 100644 (file)
@@ -203,7 +203,8 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
        struct vmbus_channel *primary_channel;
        struct vmbus_channel_relid_released msg;
 
-       vmbus_device_unregister(channel->device_obj);
+       if (channel->device_obj)
+               vmbus_device_unregister(channel->device_obj);
        memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
        msg.child_relid = channel->offermsg.child_relid;
        msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
@@ -216,7 +217,7 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
        } else {
                primary_channel = channel->primary_channel;
                spin_lock_irqsave(&primary_channel->sc_lock, flags);
-               list_del(&channel->listentry);
+               list_del(&channel->sc_list);
                spin_unlock_irqrestore(&primary_channel->sc_lock, flags);
        }
        free_channel(channel);
index 936093e0271e3c7fffccce7fe0e688c4d6f3ad01..af6edf9b19365a4938ca16f6a913a08cc6a35fa7 100644 (file)
@@ -76,10 +76,8 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
        msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
        msg->vmbus_version_requested = version;
        msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
-       msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages);
-       msg->monitor_page2 = virt_to_phys(
-                       (void *)((unsigned long)vmbus_connection.monitor_pages +
-                                PAGE_SIZE));
+       msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
+       msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
 
        /*
         * Add to list before we send the request since we may
@@ -169,9 +167,10 @@ int vmbus_connect(void)
         * Setup the monitor notification facility. The 1st page for
         * parent->child and the 2nd page for child->parent
         */
-       vmbus_connection.monitor_pages =
-       (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1);
-       if (vmbus_connection.monitor_pages == NULL) {
+       vmbus_connection.monitor_pages[0] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0);
+       vmbus_connection.monitor_pages[1] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0);
+       if ((vmbus_connection.monitor_pages[0] == NULL) ||
+           (vmbus_connection.monitor_pages[1] == NULL)) {
                ret = -ENOMEM;
                goto cleanup;
        }
@@ -229,10 +228,10 @@ cleanup:
                vmbus_connection.int_page = NULL;
        }
 
-       if (vmbus_connection.monitor_pages) {
-               free_pages((unsigned long)vmbus_connection.monitor_pages, 1);
-               vmbus_connection.monitor_pages = NULL;
-       }
+       free_pages((unsigned long)vmbus_connection.monitor_pages[0], 1);
+       free_pages((unsigned long)vmbus_connection.monitor_pages[1], 1);
+       vmbus_connection.monitor_pages[0] = NULL;
+       vmbus_connection.monitor_pages[1] = NULL;
 
        kfree(msginfo);
 
index 88f4096fa078d7f4e2ae1eeee41fd7188ab1a313..f0c5e07c25ec9f88d4bc0cee66a990da77f3636e 100644 (file)
@@ -304,7 +304,7 @@ err:
 void hv_synic_free_cpu(int cpu)
 {
        kfree(hv_context.event_dpc[cpu]);
-       if (hv_context.synic_message_page[cpu])
+       if (hv_context.synic_event_page[cpu])
                free_page((unsigned long)hv_context.synic_event_page[cpu]);
        if (hv_context.synic_message_page[cpu])
                free_page((unsigned long)hv_context.synic_message_page[cpu]);
index 273e3ddb3a20b00c5d1ae03bab6368c5fcfbc26f..62dfd246b94838e622c02bd125650a79a21c3f32 100644 (file)
@@ -97,7 +97,7 @@ static void shutdown_onchannelcallback(void *context)
        struct vmbus_channel *channel = context;
        u32 recvlen;
        u64 requestid;
-       u8  execute_shutdown = false;
+       bool execute_shutdown = false;
        u8  *shut_txf_buf = util_shutdown.recv_buffer;
 
        struct shutdown_msg_data *shutdown_msg;
index d84918fe19ab7023e4189b0cb52d98495340abd1..e05517616a06e549407f422a4713145b0f44e1de 100644 (file)
@@ -514,6 +514,13 @@ struct hv_context {
 
 extern struct hv_context hv_context;
 
+struct hv_ring_buffer_debug_info {
+       u32 current_interrupt_mask;
+       u32 current_read_index;
+       u32 current_write_index;
+       u32 bytes_avail_toread;
+       u32 bytes_avail_towrite;
+};
 
 /* Hv Interface */
 
@@ -612,7 +619,7 @@ struct vmbus_connection {
         * 2 pages - 1st page for parent->child notification and 2nd
         * is child->parent notification
         */
-       void *monitor_pages;
+       struct hv_monitor_page *monitor_pages[2];
        struct list_head chn_msg_list;
        spinlock_t channelmsg_lock;
 
index f9fe46f52cfa35d3d006260ed6a082f41466ef58..48aad4faea068e88cbe61976aa3444d9c8a80a58 100644 (file)
@@ -46,24 +46,6 @@ static struct tasklet_struct msg_dpc;
 static struct completion probe_event;
 static int irq;
 
-struct hv_device_info {
-       u32 chn_id;
-       u32 chn_state;
-       uuid_le chn_type;
-       uuid_le chn_instance;
-
-       u32 monitor_id;
-       u32 server_monitor_pending;
-       u32 server_monitor_latency;
-       u32 server_monitor_conn_id;
-       u32 client_monitor_pending;
-       u32 client_monitor_latency;
-       u32 client_monitor_conn_id;
-
-       struct hv_dev_port_info inbound;
-       struct hv_dev_port_info outbound;
-};
-
 static int vmbus_exists(void)
 {
        if (hv_acpi_dev == NULL)
@@ -72,169 +54,361 @@ static int vmbus_exists(void)
        return 0;
 }
 
+#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2)
+static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
+{
+       int i;
+       for (i = 0; i < VMBUS_ALIAS_LEN; i += 2)
+               sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
+}
 
-static void get_channel_info(struct hv_device *device,
-                            struct hv_device_info *info)
+static u8 channel_monitor_group(struct vmbus_channel *channel)
 {
-       struct vmbus_channel_debug_info debug_info;
+       return (u8)channel->offermsg.monitorid / 32;
+}
 
-       if (!device->channel)
-               return;
+static u8 channel_monitor_offset(struct vmbus_channel *channel)
+{
+       return (u8)channel->offermsg.monitorid % 32;
+}
 
-       vmbus_get_debug_info(device->channel, &debug_info);
+static u32 channel_pending(struct vmbus_channel *channel,
+                          struct hv_monitor_page *monitor_page)
+{
+       u8 monitor_group = channel_monitor_group(channel);
+       return monitor_page->trigger_group[monitor_group].pending;
+}
 
-       info->chn_id = debug_info.relid;
-       info->chn_state = debug_info.state;
-       memcpy(&info->chn_type, &debug_info.interfacetype,
-              sizeof(uuid_le));
-       memcpy(&info->chn_instance, &debug_info.interface_instance,
-              sizeof(uuid_le));
+static u32 channel_latency(struct vmbus_channel *channel,
+                          struct hv_monitor_page *monitor_page)
+{
+       u8 monitor_group = channel_monitor_group(channel);
+       u8 monitor_offset = channel_monitor_offset(channel);
+       return monitor_page->latency[monitor_group][monitor_offset];
+}
 
-       info->monitor_id = debug_info.monitorid;
+static u32 channel_conn_id(struct vmbus_channel *channel,
+                          struct hv_monitor_page *monitor_page)
+{
+       u8 monitor_group = channel_monitor_group(channel);
+       u8 monitor_offset = channel_monitor_offset(channel);
+       return monitor_page->parameter[monitor_group][monitor_offset].connectionid.u.id;
+}
 
-       info->server_monitor_pending = debug_info.servermonitor_pending;
-       info->server_monitor_latency = debug_info.servermonitor_latency;
-       info->server_monitor_conn_id = debug_info.servermonitor_connectionid;
+static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr,
+                      char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
 
-       info->client_monitor_pending = debug_info.clientmonitor_pending;
-       info->client_monitor_latency = debug_info.clientmonitor_latency;
-       info->client_monitor_conn_id = debug_info.clientmonitor_connectionid;
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n", hv_dev->channel->offermsg.child_relid);
+}
+static DEVICE_ATTR_RO(id);
 
-       info->inbound.int_mask = debug_info.inbound.current_interrupt_mask;
-       info->inbound.read_idx = debug_info.inbound.current_read_index;
-       info->inbound.write_idx = debug_info.inbound.current_write_index;
-       info->inbound.bytes_avail_toread =
-               debug_info.inbound.bytes_avail_toread;
-       info->inbound.bytes_avail_towrite =
-               debug_info.inbound.bytes_avail_towrite;
+static ssize_t state_show(struct device *dev, struct device_attribute *dev_attr,
+                         char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
 
-       info->outbound.int_mask =
-               debug_info.outbound.current_interrupt_mask;
-       info->outbound.read_idx = debug_info.outbound.current_read_index;
-       info->outbound.write_idx = debug_info.outbound.current_write_index;
-       info->outbound.bytes_avail_toread =
-               debug_info.outbound.bytes_avail_toread;
-       info->outbound.bytes_avail_towrite =
-               debug_info.outbound.bytes_avail_towrite;
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n", hv_dev->channel->state);
 }
+static DEVICE_ATTR_RO(state);
 
-#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2)
-static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
+static ssize_t monitor_id_show(struct device *dev,
+                              struct device_attribute *dev_attr, char *buf)
 {
-       int i;
-       for (i = 0; i < VMBUS_ALIAS_LEN; i += 2)
-               sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n", hv_dev->channel->offermsg.monitorid);
 }
+static DEVICE_ATTR_RO(monitor_id);
 
-/*
- * vmbus_show_device_attr - Show the device attribute in sysfs.
- *
- * This is invoked when user does a
- * "cat /sys/bus/vmbus/devices/<busdevice>/<attr name>"
- */
-static ssize_t vmbus_show_device_attr(struct device *dev,
-                                     struct device_attribute *dev_attr,
-                                     char *buf)
+static ssize_t class_id_show(struct device *dev,
+                              struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "{%pUl}\n",
+                      hv_dev->channel->offermsg.offer.if_type.b);
+}
+static DEVICE_ATTR_RO(class_id);
+
+static ssize_t device_id_show(struct device *dev,
+                             struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "{%pUl}\n",
+                      hv_dev->channel->offermsg.offer.if_instance.b);
+}
+static DEVICE_ATTR_RO(device_id);
+
+static ssize_t modalias_show(struct device *dev,
+                            struct device_attribute *dev_attr, char *buf)
 {
        struct hv_device *hv_dev = device_to_hv_device(dev);
-       struct hv_device_info *device_info;
        char alias_name[VMBUS_ALIAS_LEN + 1];
-       int ret = 0;
 
-       device_info = kzalloc(sizeof(struct hv_device_info), GFP_KERNEL);
-       if (!device_info)
-               return ret;
+       print_alias_name(hv_dev, alias_name);
+       return sprintf(buf, "vmbus:%s\n", alias_name);
+}
+static DEVICE_ATTR_RO(modalias);
 
-       get_channel_info(hv_dev, device_info);
-
-       if (!strcmp(dev_attr->attr.name, "class_id")) {
-               ret = sprintf(buf, "{%pUl}\n", device_info->chn_type.b);
-       } else if (!strcmp(dev_attr->attr.name, "device_id")) {
-               ret = sprintf(buf, "{%pUl}\n", device_info->chn_instance.b);
-       } else if (!strcmp(dev_attr->attr.name, "modalias")) {
-               print_alias_name(hv_dev, alias_name);
-               ret = sprintf(buf, "vmbus:%s\n", alias_name);
-       } else if (!strcmp(dev_attr->attr.name, "state")) {
-               ret = sprintf(buf, "%d\n", device_info->chn_state);
-       } else if (!strcmp(dev_attr->attr.name, "id")) {
-               ret = sprintf(buf, "%d\n", device_info->chn_id);
-       } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) {
-               ret = sprintf(buf, "%d\n", device_info->outbound.int_mask);
-       } else if (!strcmp(dev_attr->attr.name, "out_read_index")) {
-               ret = sprintf(buf, "%d\n", device_info->outbound.read_idx);
-       } else if (!strcmp(dev_attr->attr.name, "out_write_index")) {
-               ret = sprintf(buf, "%d\n", device_info->outbound.write_idx);
-       } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->outbound.bytes_avail_toread);
-       } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->outbound.bytes_avail_towrite);
-       } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) {
-               ret = sprintf(buf, "%d\n", device_info->inbound.int_mask);
-       } else if (!strcmp(dev_attr->attr.name, "in_read_index")) {
-               ret = sprintf(buf, "%d\n", device_info->inbound.read_idx);
-       } else if (!strcmp(dev_attr->attr.name, "in_write_index")) {
-               ret = sprintf(buf, "%d\n", device_info->inbound.write_idx);
-       } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->inbound.bytes_avail_toread);
-       } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->inbound.bytes_avail_towrite);
-       } else if (!strcmp(dev_attr->attr.name, "monitor_id")) {
-               ret = sprintf(buf, "%d\n", device_info->monitor_id);
-       } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) {
-               ret = sprintf(buf, "%d\n", device_info->server_monitor_pending);
-       } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) {
-               ret = sprintf(buf, "%d\n", device_info->server_monitor_latency);
-       } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->server_monitor_conn_id);
-       } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) {
-               ret = sprintf(buf, "%d\n", device_info->client_monitor_pending);
-       } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) {
-               ret = sprintf(buf, "%d\n", device_info->client_monitor_latency);
-       } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->client_monitor_conn_id);
-       }
+static ssize_t server_monitor_pending_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
 
-       kfree(device_info);
-       return ret;
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_pending(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[1]));
+}
+static DEVICE_ATTR_RO(server_monitor_pending);
+
+static ssize_t client_monitor_pending_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_pending(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[1]));
+}
+static DEVICE_ATTR_RO(client_monitor_pending);
+
+static ssize_t server_monitor_latency_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_latency(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[0]));
+}
+static DEVICE_ATTR_RO(server_monitor_latency);
+
+static ssize_t client_monitor_latency_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_latency(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[1]));
+}
+static DEVICE_ATTR_RO(client_monitor_latency);
+
+static ssize_t server_monitor_conn_id_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_conn_id(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[0]));
+}
+static DEVICE_ATTR_RO(server_monitor_conn_id);
+
+static ssize_t client_monitor_conn_id_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_conn_id(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[1]));
+}
+static DEVICE_ATTR_RO(client_monitor_conn_id);
+
+static ssize_t out_intr_mask_show(struct device *dev,
+                                 struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.current_interrupt_mask);
+}
+static DEVICE_ATTR_RO(out_intr_mask);
+
+static ssize_t out_read_index_show(struct device *dev,
+                                  struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.current_read_index);
+}
+static DEVICE_ATTR_RO(out_read_index);
+
+static ssize_t out_write_index_show(struct device *dev,
+                                   struct device_attribute *dev_attr,
+                                   char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.current_write_index);
+}
+static DEVICE_ATTR_RO(out_write_index);
+
+static ssize_t out_read_bytes_avail_show(struct device *dev,
+                                        struct device_attribute *dev_attr,
+                                        char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.bytes_avail_toread);
 }
+static DEVICE_ATTR_RO(out_read_bytes_avail);
+
+static ssize_t out_write_bytes_avail_show(struct device *dev,
+                                         struct device_attribute *dev_attr,
+                                         char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.bytes_avail_towrite);
+}
+static DEVICE_ATTR_RO(out_write_bytes_avail);
+
+static ssize_t in_intr_mask_show(struct device *dev,
+                                struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.current_interrupt_mask);
+}
+static DEVICE_ATTR_RO(in_intr_mask);
+
+static ssize_t in_read_index_show(struct device *dev,
+                                 struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.current_read_index);
+}
+static DEVICE_ATTR_RO(in_read_index);
+
+static ssize_t in_write_index_show(struct device *dev,
+                                  struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.current_write_index);
+}
+static DEVICE_ATTR_RO(in_write_index);
+
+static ssize_t in_read_bytes_avail_show(struct device *dev,
+                                       struct device_attribute *dev_attr,
+                                       char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.bytes_avail_toread);
+}
+static DEVICE_ATTR_RO(in_read_bytes_avail);
+
+static ssize_t in_write_bytes_avail_show(struct device *dev,
+                                        struct device_attribute *dev_attr,
+                                        char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.bytes_avail_towrite);
+}
+static DEVICE_ATTR_RO(in_write_bytes_avail);
 
 /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */
-static struct device_attribute vmbus_device_attrs[] = {
-       __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(modalias, S_IRUGO, vmbus_show_device_attr, NULL),
-
-       __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
-
-       __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
-
-       __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
-
-       __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR_NULL
+static struct attribute *vmbus_attrs[] = {
+       &dev_attr_id.attr,
+       &dev_attr_state.attr,
+       &dev_attr_monitor_id.attr,
+       &dev_attr_class_id.attr,
+       &dev_attr_device_id.attr,
+       &dev_attr_modalias.attr,
+       &dev_attr_server_monitor_pending.attr,
+       &dev_attr_client_monitor_pending.attr,
+       &dev_attr_server_monitor_latency.attr,
+       &dev_attr_client_monitor_latency.attr,
+       &dev_attr_server_monitor_conn_id.attr,
+       &dev_attr_client_monitor_conn_id.attr,
+       &dev_attr_out_intr_mask.attr,
+       &dev_attr_out_read_index.attr,
+       &dev_attr_out_write_index.attr,
+       &dev_attr_out_read_bytes_avail.attr,
+       &dev_attr_out_write_bytes_avail.attr,
+       &dev_attr_in_intr_mask.attr,
+       &dev_attr_in_read_index.attr,
+       &dev_attr_in_write_index.attr,
+       &dev_attr_in_read_bytes_avail.attr,
+       &dev_attr_in_write_bytes_avail.attr,
+       NULL,
 };
-
+ATTRIBUTE_GROUPS(vmbus);
 
 /*
  * vmbus_uevent - add uevent for our device
@@ -383,7 +557,7 @@ static struct bus_type  hv_bus = {
        .remove =               vmbus_remove,
        .probe =                vmbus_probe,
        .uevent =               vmbus_uevent,
-       .dev_attrs =    vmbus_device_attrs,
+       .dev_groups =           vmbus_groups,
 };
 
 static const char *driver_name = "hyperv";
index 883ffacaf45ac4bbd67ca8fe1f9cf576149665ad..84a6a9e08d64d450ef2828e72d75da8949cf5052 100644 (file)
@@ -25,6 +25,7 @@ static ssize_t media_show(struct device *dev, struct device_attribute *attr,
        ide_drive_t *drive = to_ide_device(dev);
        return sprintf(buf, "%s\n", ide_media_string(drive));
 }
+static DEVICE_ATTR_RO(media);
 
 static ssize_t drivename_show(struct device *dev, struct device_attribute *attr,
                              char *buf)
@@ -32,6 +33,7 @@ static ssize_t drivename_show(struct device *dev, struct device_attribute *attr,
        ide_drive_t *drive = to_ide_device(dev);
        return sprintf(buf, "%s\n", drive->name);
 }
+static DEVICE_ATTR_RO(drivename);
 
 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
                             char *buf)
@@ -39,6 +41,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
        ide_drive_t *drive = to_ide_device(dev);
        return sprintf(buf, "ide:m-%s\n", ide_media_string(drive));
 }
+static DEVICE_ATTR_RO(modalias);
 
 static ssize_t model_show(struct device *dev, struct device_attribute *attr,
                          char *buf)
@@ -46,6 +49,7 @@ static ssize_t model_show(struct device *dev, struct device_attribute *attr,
        ide_drive_t *drive = to_ide_device(dev);
        return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_PROD]);
 }
+static DEVICE_ATTR_RO(model);
 
 static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
                             char *buf)
@@ -53,6 +57,7 @@ static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
        ide_drive_t *drive = to_ide_device(dev);
        return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_FW_REV]);
 }
+static DEVICE_ATTR_RO(firmware);
 
 static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
                           char *buf)
@@ -60,16 +65,28 @@ static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
        ide_drive_t *drive = to_ide_device(dev);
        return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_SERNO]);
 }
+static DEVICE_ATTR(serial, 0400, serial_show, NULL);
+
+static DEVICE_ATTR(unload_heads, 0644, ide_park_show, ide_park_store);
+
+static struct attribute *ide_attrs[] = {
+       &dev_attr_media.attr,
+       &dev_attr_drivename.attr,
+       &dev_attr_modalias.attr,
+       &dev_attr_model.attr,
+       &dev_attr_firmware.attr,
+       &dev_attr_serial.attr,
+       &dev_attr_unload_heads.attr,
+       NULL,
+};
+
+static const struct attribute_group ide_attr_group = {
+       .attrs = ide_attrs,
+};
 
-struct device_attribute ide_dev_attrs[] = {
-       __ATTR_RO(media),
-       __ATTR_RO(drivename),
-       __ATTR_RO(modalias),
-       __ATTR_RO(model),
-       __ATTR_RO(firmware),
-       __ATTR(serial, 0400, serial_show, NULL),
-       __ATTR(unload_heads, 0644, ide_park_show, ide_park_store),
-       __ATTR_NULL
+const struct attribute_group *ide_dev_groups[] = {
+       &ide_attr_group,
+       NULL,
 };
 
 static ssize_t store_delete_devices(struct device *portdev,
index fa896210ed7b2af40136dd7784737929fbf80b28..2ce6268a27348c9bfab9910da2ea3f9a364b1a44 100644 (file)
@@ -158,7 +158,7 @@ struct bus_type ide_bus_type = {
        .probe          = generic_ide_probe,
        .remove         = generic_ide_remove,
        .shutdown       = generic_ide_shutdown,
-       .dev_attrs      = ide_dev_attrs,
+       .dev_groups     = ide_dev_groups,
        .suspend        = generic_ide_suspend,
        .resume         = generic_ide_resume,
 };
index 5ceda710f516bc4721e14961a504fa7e2c3054a9..b84791f03a27f5d174b60680f77c59de06bb542a 100644 (file)
@@ -31,6 +31,17 @@ config INFINIBAND_USER_ACCESS
          libibverbs, libibcm and a hardware driver library from
          <http://www.openfabrics.org/git/>.
 
+config INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
+       bool "Experimental and unstable ABI for userspace access to flow steering verbs"
+       depends on INFINIBAND_USER_ACCESS
+       depends on STAGING
+       ---help---
+         The final ABI for userspace access to flow steering verbs
+         has not been defined.  To use the current ABI, *WHICH WILL
+         CHANGE IN THE FUTURE*, say Y here.
+
+         If unsure, say N.
+
 config INFINIBAND_USER_MEM
        bool
        depends on INFINIBAND_USER_ACCESS != n
index d040b877475f3f04f9f6c2fea63ba772fbd9586c..d8f9c6c272d732a898b334be0c3034aadf37b275 100644 (file)
@@ -217,7 +217,9 @@ IB_UVERBS_DECLARE_CMD(destroy_srq);
 IB_UVERBS_DECLARE_CMD(create_xsrq);
 IB_UVERBS_DECLARE_CMD(open_xrcd);
 IB_UVERBS_DECLARE_CMD(close_xrcd);
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 IB_UVERBS_DECLARE_CMD(create_flow);
 IB_UVERBS_DECLARE_CMD(destroy_flow);
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 #endif /* UVERBS_H */
index f2b81b9ee0d6849c67693e50c351721c64df27fe..2f0f01b70e3bd22c538cd3a4081be0a9ec3f790f 100644 (file)
@@ -54,7 +54,9 @@ static struct uverbs_lock_class qp_lock_class = { .name = "QP-uobj" };
 static struct uverbs_lock_class ah_lock_class  = { .name = "AH-uobj" };
 static struct uverbs_lock_class srq_lock_class = { .name = "SRQ-uobj" };
 static struct uverbs_lock_class xrcd_lock_class = { .name = "XRCD-uobj" };
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 static struct uverbs_lock_class rule_lock_class = { .name = "RULE-uobj" };
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 #define INIT_UDATA(udata, ibuf, obuf, ilen, olen)                      \
        do {                                                            \
@@ -2599,6 +2601,7 @@ out_put:
        return ret ? ret : in_len;
 }
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 static int kern_spec_to_ib_spec(struct ib_kern_spec *kern_spec,
                                union ib_flow_spec *ib_spec)
 {
@@ -2824,6 +2827,7 @@ ssize_t ib_uverbs_destroy_flow(struct ib_uverbs_file *file,
 
        return ret ? ret : in_len;
 }
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
                                struct ib_uverbs_create_xsrq *cmd,
index 75ad86c4abf82a86572d9ca8b35d52a7f9d2d4ce..2df31f68ea0905ef06ac1ed054348cf669cfaa61 100644 (file)
@@ -115,8 +115,10 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file,
        [IB_USER_VERBS_CMD_CLOSE_XRCD]          = ib_uverbs_close_xrcd,
        [IB_USER_VERBS_CMD_CREATE_XSRQ]         = ib_uverbs_create_xsrq,
        [IB_USER_VERBS_CMD_OPEN_QP]             = ib_uverbs_open_qp,
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
        [IB_USER_VERBS_CMD_CREATE_FLOW]         = ib_uverbs_create_flow,
        [IB_USER_VERBS_CMD_DESTROY_FLOW]        = ib_uverbs_destroy_flow
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 };
 
 static void ib_uverbs_add_one(struct ib_device *device);
@@ -605,6 +607,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
        if (!(file->device->ib_dev->uverbs_cmd_mask & (1ull << hdr.command)))
                return -ENOSYS;
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
        if (hdr.command >= IB_USER_VERBS_CMD_THRESHOLD) {
                struct ib_uverbs_cmd_hdr_ex hdr_ex;
 
@@ -621,6 +624,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
                                                     (hdr_ex.out_words +
                                                      hdr_ex.provider_out_words) * 4);
        } else {
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
                if (hdr.in_words * 4 != count)
                        return -EINVAL;
 
@@ -628,7 +632,9 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
                                                     buf + sizeof(hdr),
                                                     hdr.in_words * 4,
                                                     hdr.out_words * 4);
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
        }
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 }
 
 static int ib_uverbs_mmap(struct file *filp, struct vm_area_struct *vma)
index d6c5a73becf40ecfc0f177422f34de1f14247f32..f0612645de998f6bcba2dfa0ae34a2b51483af38 100644 (file)
@@ -1691,9 +1691,11 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
                ibdev->ib_dev.create_flow       = mlx4_ib_create_flow;
                ibdev->ib_dev.destroy_flow      = mlx4_ib_destroy_flow;
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
                ibdev->ib_dev.uverbs_cmd_mask   |=
                        (1ull << IB_USER_VERBS_CMD_CREATE_FLOW) |
                        (1ull << IB_USER_VERBS_CMD_DESTROY_FLOW);
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
        }
 
        mlx4_ib_alloc_eqs(dev, ibdev);
index 3591855cc5b54caeca1e9675757a43579537b4be..6df23502059a44eccd32c725477cd5fee1ef2923 100644 (file)
@@ -594,7 +594,7 @@ isert_connect_release(struct isert_conn *isert_conn)
 
        pr_debug("Entering isert_connect_release(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
 
-       if (device->use_frwr)
+       if (device && device->use_frwr)
                isert_conn_free_frwr_pool(isert_conn);
 
        if (isert_conn->conn_qp) {
index 922a7fea2ce6758f39d5945a74d2953efcc672f5..24c41ba7d4e01dcf852050d3f1d8ef27b5f0a22f 100644 (file)
@@ -422,14 +422,15 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent)
  * Gameport port operations
  */
 
-static ssize_t gameport_show_description(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t gameport_description_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct gameport *gameport = to_gameport_port(dev);
 
        return sprintf(buf, "%s\n", gameport->name);
 }
+static DEVICE_ATTR(description, S_IRUGO, gameport_description_show, NULL);
 
-static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct gameport *gameport = to_gameport_port(dev);
        struct device_driver *drv;
@@ -457,12 +458,14 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut
 
        return error ? error : count;
 }
+static DEVICE_ATTR_WO(drvctl);
 
-static struct device_attribute gameport_device_attrs[] = {
-       __ATTR(description, S_IRUGO, gameport_show_description, NULL),
-       __ATTR(drvctl, S_IWUSR, NULL, gameport_rebind_driver),
-       __ATTR_NULL
+static struct attribute *gameport_device_attrs[] = {
+       &dev_attr_description.attr,
+       &dev_attr_drvctl.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(gameport_device);
 
 static void gameport_release_port(struct device *dev)
 {
@@ -750,7 +753,7 @@ static int gameport_bus_match(struct device *dev, struct device_driver *drv)
 
 static struct bus_type gameport_bus = {
        .name           = "gameport",
-       .dev_attrs      = gameport_device_attrs,
+       .dev_groups     = gameport_device_groups,
        .drv_groups     = gameport_driver_groups,
        .match          = gameport_bus_match,
        .probe          = gameport_driver_probe,
index c0446992892533b24d3853d043cf088451db5662..e75d015024a15c7fb6eb3074299e234a69916594 100644 (file)
@@ -1734,6 +1734,7 @@ EXPORT_SYMBOL_GPL(input_class);
  */
 struct input_dev *input_allocate_device(void)
 {
+       static atomic_t input_no = ATOMIC_INIT(0);
        struct input_dev *dev;
 
        dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
@@ -1743,9 +1744,13 @@ struct input_dev *input_allocate_device(void)
                device_initialize(&dev->dev);
                mutex_init(&dev->mutex);
                spin_lock_init(&dev->event_lock);
+               init_timer(&dev->timer);
                INIT_LIST_HEAD(&dev->h_list);
                INIT_LIST_HEAD(&dev->node);
 
+               dev_set_name(&dev->dev, "input%ld",
+                            (unsigned long) atomic_inc_return(&input_no) - 1);
+
                __module_get(THIS_MODULE);
        }
 
@@ -2019,7 +2024,6 @@ static void devm_input_device_unregister(struct device *dev, void *res)
  */
 int input_register_device(struct input_dev *dev)
 {
-       static atomic_t input_no = ATOMIC_INIT(0);
        struct input_devres *devres = NULL;
        struct input_handler *handler;
        unsigned int packet_size;
@@ -2059,7 +2063,6 @@ int input_register_device(struct input_dev *dev)
         * If delay and period are pre-set by the driver, then autorepeating
         * is handled by the driver itself and we don't do it in input.c.
         */
-       init_timer(&dev->timer);
        if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
                dev->timer.data = (long) dev;
                dev->timer.function = input_repeat_key;
@@ -2073,9 +2076,6 @@ int input_register_device(struct input_dev *dev)
        if (!dev->setkeycode)
                dev->setkeycode = input_default_setkeycode;
 
-       dev_set_name(&dev->dev, "input%ld",
-                    (unsigned long) atomic_inc_return(&input_no) - 1);
-
        error = device_add(&dev->dev);
        if (error)
                goto err_free_vals;
index 134c3b404a54d22a5b7b425c22fc88ea4949cecf..a2e758d27584c0117a5c941d36b8042ab20f425f 100644 (file)
@@ -786,10 +786,17 @@ static int pxa27x_keypad_probe(struct platform_device *pdev)
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
        input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 
-       if (pdata)
+       if (pdata) {
                error = pxa27x_keypad_build_keycode(keypad);
-       else
+       } else {
                error = pxa27x_keypad_build_keycode_from_dt(keypad);
+               /*
+                * Data that we get from DT resides in dynamically
+                * allocated memory so we need to update our pdata
+                * pointer.
+                */
+               pdata = keypad->pdata;
+       }
        if (error) {
                dev_err(&pdev->dev, "failed to build keycode\n");
                goto failed_put_clk;
index 082684e7f390f7d9f50201df3e5ddd325f2d0256..9365535ba7f157b98138f6f0defb14d9e1ae782a 100644 (file)
@@ -351,7 +351,9 @@ static void cm109_urb_irq_callback(struct urb *urb)
        if (status) {
                if (status == -ESHUTDOWN)
                        return;
-               dev_err(&dev->intf->dev, "%s: urb status %d\n", __func__, status);
+               dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
+                                   __func__, status);
+               goto out;
        }
 
        /* Special keys */
@@ -418,8 +420,12 @@ static void cm109_urb_ctl_callback(struct urb *urb)
             dev->ctl_data->byte[2],
             dev->ctl_data->byte[3]);
 
-       if (status)
-               dev_err(&dev->intf->dev, "%s: urb status %d\n", __func__, status);
+       if (status) {
+               if (status == -ESHUTDOWN)
+                       return;
+               dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
+                                   __func__, status);
+       }
 
        spin_lock(&dev->ctl_submit_lock);
 
@@ -427,7 +433,7 @@ static void cm109_urb_ctl_callback(struct urb *urb)
 
        if (likely(!dev->shutdown)) {
 
-               if (dev->buzzer_pending) {
+               if (dev->buzzer_pending || status) {
                        dev->buzzer_pending = 0;
                        dev->ctl_urb_pending = 1;
                        cm109_submit_buzz_toggle(dev);
index 7c5d72a6a26a3400fbedfcbe19e37e1abb298f1f..83658472ad2511735b2e02c8ee645858385d58f0 100644 (file)
@@ -103,6 +103,7 @@ static const struct alps_model_info alps_model_data[] = {
        /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
        { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
+       { { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_DUALPOINT },              /* Dell XT2 */
        { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },           /* Dell Vostro 1400 */
        { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },                            /* Toshiba Tecra A11-11L */
index 78e4de42efaacec53c29f740fafe490dead2af84..52c9ebf94729ff5bf6531cc7773902ece632d03b 100644 (file)
@@ -223,21 +223,26 @@ static int i8042_flush(void)
 {
        unsigned long flags;
        unsigned char data, str;
-       int i = 0;
+       int count = 0;
+       int retval = 0;
 
        spin_lock_irqsave(&i8042_lock, flags);
 
-       while (((str = i8042_read_status()) & I8042_STR_OBF) && (i < I8042_BUFFER_SIZE)) {
-               udelay(50);
-               data = i8042_read_data();
-               i++;
-               dbg("%02x <- i8042 (flush, %s)\n",
-                   data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
+       while ((str = i8042_read_status()) & I8042_STR_OBF) {
+               if (count++ < I8042_BUFFER_SIZE) {
+                       udelay(50);
+                       data = i8042_read_data();
+                       dbg("%02x <- i8042 (flush, %s)\n",
+                           data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
+               } else {
+                       retval = -EIO;
+                       break;
+               }
        }
 
        spin_unlock_irqrestore(&i8042_lock, flags);
 
-       return i;
+       return retval;
 }
 
 /*
@@ -849,7 +854,7 @@ static int __init i8042_check_aux(void)
 
 static int i8042_controller_check(void)
 {
-       if (i8042_flush() == I8042_BUFFER_SIZE) {
+       if (i8042_flush()) {
                pr_err("No controller found\n");
                return -ENODEV;
        }
index 2b56855c2c773d2f47ec0ab57e147a761092b39b..98707fb2cb5d672b6b5bcdc8b787bb37c90c9989 100644 (file)
@@ -365,7 +365,7 @@ static ssize_t serio_show_description(struct device *dev, struct device_attribut
        return sprintf(buf, "%s\n", serio->name);
 }
 
-static ssize_t serio_show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
 
@@ -373,54 +373,31 @@ static ssize_t serio_show_modalias(struct device *dev, struct device_attribute *
                        serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
 }
 
-static ssize_t serio_show_id_type(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%02x\n", serio->id.type);
 }
 
-static ssize_t serio_show_id_proto(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t proto_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%02x\n", serio->id.proto);
 }
 
-static ssize_t serio_show_id_id(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%02x\n", serio->id.id);
 }
 
-static ssize_t serio_show_id_extra(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t extra_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%02x\n", serio->id.extra);
 }
 
-static DEVICE_ATTR(type, S_IRUGO, serio_show_id_type, NULL);
-static DEVICE_ATTR(proto, S_IRUGO, serio_show_id_proto, NULL);
-static DEVICE_ATTR(id, S_IRUGO, serio_show_id_id, NULL);
-static DEVICE_ATTR(extra, S_IRUGO, serio_show_id_extra, NULL);
-
-static struct attribute *serio_device_id_attrs[] = {
-       &dev_attr_type.attr,
-       &dev_attr_proto.attr,
-       &dev_attr_id.attr,
-       &dev_attr_extra.attr,
-       NULL
-};
-
-static struct attribute_group serio_id_attr_group = {
-       .name   = "id",
-       .attrs  = serio_device_id_attrs,
-};
-
-static const struct attribute_group *serio_device_attr_groups[] = {
-       &serio_id_attr_group,
-       NULL
-};
-
-static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct serio *serio = to_serio_port(dev);
        struct device_driver *drv;
@@ -474,14 +451,36 @@ static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *
        return retval;
 }
 
-static struct device_attribute serio_device_attrs[] = {
-       __ATTR(description, S_IRUGO, serio_show_description, NULL),
-       __ATTR(modalias, S_IRUGO, serio_show_modalias, NULL),
-       __ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver),
-       __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode),
-       __ATTR_NULL
+static DEVICE_ATTR_RO(type);
+static DEVICE_ATTR_RO(proto);
+static DEVICE_ATTR_RO(id);
+static DEVICE_ATTR_RO(extra);
+static DEVICE_ATTR_RO(modalias);
+static DEVICE_ATTR_WO(drvctl);
+static DEVICE_ATTR(description, S_IRUGO, serio_show_description, NULL);
+static DEVICE_ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode);
+
+static struct attribute *serio_device_id_attrs[] = {
+       &dev_attr_type.attr,
+       &dev_attr_proto.attr,
+       &dev_attr_id.attr,
+       &dev_attr_extra.attr,
+       &dev_attr_modalias.attr,
+       &dev_attr_description.attr,
+       &dev_attr_drvctl.attr,
+       &dev_attr_bind_mode.attr,
+       NULL
 };
 
+static struct attribute_group serio_id_attr_group = {
+       .name   = "id",
+       .attrs  = serio_device_id_attrs,
+};
+
+static const struct attribute_group *serio_device_attr_groups[] = {
+       &serio_id_attr_group,
+       NULL
+};
 
 static void serio_release_port(struct device *dev)
 {
@@ -996,7 +995,6 @@ EXPORT_SYMBOL(serio_interrupt);
 
 static struct bus_type serio_bus = {
        .name           = "serio",
-       .dev_attrs      = serio_device_attrs,
        .drv_groups     = serio_driver_groups,
        .match          = serio_bus_match,
        .uevent         = serio_uevent,
index 79b69ea47f747abd035d2eb44cad9d724699ddd7..e53416a4d7f3275ea2fd8f53f6a55dcbb6de02ad 100644 (file)
@@ -1031,6 +1031,7 @@ static void wacom_destroy_leds(struct wacom *wacom)
 }
 
 static enum power_supply_property wacom_battery_props[] = {
+       POWER_SUPPLY_PROP_SCOPE,
        POWER_SUPPLY_PROP_CAPACITY
 };
 
@@ -1042,6 +1043,9 @@ static int wacom_battery_get_property(struct power_supply *psy,
        int ret = 0;
 
        switch (psp) {
+               case POWER_SUPPLY_PROP_SCOPE:
+                       val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+                       break;
                case POWER_SUPPLY_PROP_CAPACITY:
                        val->intval =
                                wacom->wacom_wac.battery_capacity * 100 / 31;
index b2aa503c16b1fb08c7d94817c533794d6d9375bd..c59b797eeafaa4f6258552389437cb8d113e1642 100644 (file)
@@ -2054,6 +2054,12 @@ static const struct wacom_features wacom_features_0x101 =
 static const struct wacom_features wacom_features_0x10D =
        { "Wacom ISDv4 10D",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,
          0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x10E =
+       { "Wacom ISDv4 10E",      WACOM_PKGLEN_MTTPC,     27760, 15694,  255,
+         0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x10F =
+       { "Wacom ISDv4 10F",      WACOM_PKGLEN_MTTPC,     27760, 15694,  255,
+         0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x4001 =
        { "Wacom ISDv4 4001",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,
          0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -2248,6 +2254,8 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x100) },
        { USB_DEVICE_WACOM(0x101) },
        { USB_DEVICE_WACOM(0x10D) },
+       { USB_DEVICE_WACOM(0x10E) },
+       { USB_DEVICE_WACOM(0x10F) },
        { USB_DEVICE_WACOM(0x300) },
        { USB_DEVICE_WACOM(0x301) },
        { USB_DEVICE_WACOM(0x304) },
index 6e066c53acce7fcd0fc5f36193f8b4c05a25ebb5..d0016ba469edeed10c58fd1ce93c5615dc2735c4 100644 (file)
@@ -180,20 +180,28 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 
 ipack_device_attr(id_format, "0x%hhu\n");
 
-static struct device_attribute ipack_dev_attrs[] = {
-       __ATTR_RO(id),
-       __ATTR_RO(id_device),
-       __ATTR_RO(id_format),
-       __ATTR_RO(id_vendor),
-       __ATTR_RO(modalias),
+static DEVICE_ATTR_RO(id);
+static DEVICE_ATTR_RO(id_device);
+static DEVICE_ATTR_RO(id_format);
+static DEVICE_ATTR_RO(id_vendor);
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *ipack_attrs[] = {
+       &dev_attr_id.attr,
+       &dev_attr_id_device.attr,
+       &dev_attr_id_format.attr,
+       &dev_attr_id_vendor.attr,
+       &dev_attr_modalias.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(ipack);
 
 static struct bus_type ipack_bus_type = {
        .name      = "ipack",
        .probe     = ipack_bus_probe,
        .match     = ipack_bus_match,
        .remove    = ipack_bus_remove,
-       .dev_attrs = ipack_dev_attrs,
+       .dev_groups = ipack_groups,
        .uevent    = ipack_uevent,
 };
 
index b6a74bcbb08f8800bae2aa57888498a46eb85f69..2a7f0dd6abab4d6bb21ba028b5b3a25721080a2c 100644 (file)
@@ -1000,7 +1000,7 @@ static void request_write(struct cached_dev *dc, struct search *s)
 
                if (bio->bi_rw & REQ_FLUSH) {
                        /* Also need to send a flush to the backing device */
-                       struct bio *flush = bio_alloc_bioset(0, GFP_NOIO,
+                       struct bio *flush = bio_alloc_bioset(GFP_NOIO, 0,
                                                             dc->disk.bio_split);
 
                        flush->bi_rw    = WRITE_FLUSH;
index a7fd82133b12b18139e57c50d382b094d88951c4..12dc29ba7399c8188da2b0ddffcf659eb46115f7 100644 (file)
@@ -1654,9 +1654,9 @@ int bitmap_create(struct mddev *mddev)
        bitmap->mddev = mddev;
 
        if (mddev->kobj.sd)
-               bm = sysfs_get_dirent(mddev->kobj.sd, NULL, "bitmap");
+               bm = sysfs_get_dirent(mddev->kobj.sd, "bitmap");
        if (bm) {
-               bitmap->sysfs_can_clear = sysfs_get_dirent(bm, NULL, "can_clear");
+               bitmap->sysfs_can_clear = sysfs_get_dirent(bm, "can_clear");
                sysfs_put(bm);
        } else
                bitmap->sysfs_can_clear = NULL;
index adf4d7e1d5e15233a67e7108dc24b8a7bb6efeb1..2445fece926339bf7a4566ffefd40a9d792eba07 100644 (file)
@@ -3555,7 +3555,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
                        printk(KERN_WARNING
                               "md: cannot register extra attributes for %s\n",
                               mdname(mddev));
-               mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, NULL, "sync_action");
+               mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, "sync_action");
        }               
        if (mddev->pers->sync_request != NULL &&
            pers->sync_request == NULL) {
@@ -8111,6 +8111,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
        u64 *p;
        int lo, hi;
        int rv = 1;
+       unsigned long flags;
 
        if (bb->shift < 0)
                /* badblocks are disabled */
@@ -8125,7 +8126,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
                sectors = next - s;
        }
 
-       write_seqlock_irq(&bb->lock);
+       write_seqlock_irqsave(&bb->lock, flags);
 
        p = bb->page;
        lo = 0;
@@ -8241,7 +8242,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
        bb->changed = 1;
        if (!acknowledged)
                bb->unacked_exist = 1;
-       write_sequnlock_irq(&bb->lock);
+       write_sequnlock_irqrestore(&bb->lock, flags);
 
        return rv;
 }
index 608050c43f17e9d7688a9b3d1b749b7971d47c33..b0051f2fbc0c84b735a711679c9835fc15d3b27b 100644 (file)
@@ -501,7 +501,7 @@ extern struct attribute_group md_bitmap_group;
 static inline struct sysfs_dirent *sysfs_get_dirent_safe(struct sysfs_dirent *sd, char *name)
 {
        if (sd)
-               return sysfs_get_dirent(sd, NULL, name);
+               return sysfs_get_dirent(sd, name);
        return sd;
 }
 static inline void sysfs_notify_dirent_safe(struct sysfs_dirent *sd)
index d60412c7f9952a350e85002f6578bd66ee416a44..aacf6bf352d87978a15633b05a0d51d3579ce16d 100644 (file)
@@ -1479,6 +1479,7 @@ static int raid1_spare_active(struct mddev *mddev)
                        }
                }
                if (rdev
+                   && rdev->recovery_offset == MaxSector
                    && !test_bit(Faulty, &rdev->flags)
                    && !test_and_set_bit(In_sync, &rdev->flags)) {
                        count++;
index df7b0a06b0ea2d820aec6ad5a752a13bb64e13e8..73dc8a377522e19af92c9bed26519a479f36dbea 100644 (file)
@@ -1782,6 +1782,7 @@ static int raid10_spare_active(struct mddev *mddev)
                        }
                        sysfs_notify_dirent_safe(tmp->replacement->sysfs_state);
                } else if (tmp->rdev
+                          && tmp->rdev->recovery_offset == MaxSector
                           && !test_bit(Faulty, &tmp->rdev->flags)
                           && !test_and_set_bit(In_sync, &tmp->rdev->flags)) {
                        count++;
index 7ff4f252ca1a42943252a9e19b975da5b2f8ce9b..f8b9068439267a3539d671e6e59ceb0b47b90547 100644 (file)
@@ -778,6 +778,12 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
                        bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
                        bi->bi_io_vec[0].bv_offset = 0;
                        bi->bi_size = STRIPE_SIZE;
+                       /*
+                        * If this is discard request, set bi_vcnt 0. We don't
+                        * want to confuse SCSI because SCSI will replace payload
+                        */
+                       if (rw & REQ_DISCARD)
+                               bi->bi_vcnt = 0;
                        if (rrdev)
                                set_bit(R5_DOUBLE_LOCKED, &sh->dev[i].flags);
 
@@ -816,6 +822,12 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
                        rbi->bi_io_vec[0].bv_len = STRIPE_SIZE;
                        rbi->bi_io_vec[0].bv_offset = 0;
                        rbi->bi_size = STRIPE_SIZE;
+                       /*
+                        * If this is discard request, set bi_vcnt 0. We don't
+                        * want to confuse SCSI because SCSI will replace payload
+                        */
+                       if (rw & REQ_DISCARD)
+                               rbi->bi_vcnt = 0;
                        if (conf->mddev->gendisk)
                                trace_block_bio_remap(bdev_get_queue(rbi->bi_bdev),
                                                      rbi, disk_devt(conf->mddev->gendisk),
@@ -2910,6 +2922,14 @@ static void handle_stripe_clean_event(struct r5conf *conf,
                }
                /* now that discard is done we can proceed with any sync */
                clear_bit(STRIPE_DISCARD, &sh->state);
+               /*
+                * SCSI discard will change some bio fields and the stripe has
+                * no updated data, so remove it from hash list and the stripe
+                * will be reinitialized
+                */
+               spin_lock_irq(&conf->device_lock);
+               remove_hash(sh);
+               spin_unlock_irq(&conf->device_lock);
                if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state))
                        set_bit(STRIPE_HANDLE, &sh->state);
 
index 2521f7e23018f70a33a9ad43e1bb26e79e037ab8..e79749cfec814b75f0ac84d2f5ad881399a79a75 100644 (file)
@@ -912,14 +912,8 @@ static int tda10071_init(struct dvb_frontend *fe)
                { 0xd5, 0x03, 0x03 },
        };
 
-       /* firmware status */
-       ret = tda10071_rd_reg(priv, 0x51, &tmp);
-       if (ret)
-               goto error;
-
-       if (!tmp) {
+       if (priv->warm) {
                /* warm state - wake up device from sleep */
-               priv->warm = 1;
 
                for (i = 0; i < ARRAY_SIZE(tab); i++) {
                        ret = tda10071_wr_reg_mask(priv, tab[i].reg,
@@ -937,7 +931,6 @@ static int tda10071_init(struct dvb_frontend *fe)
                        goto error;
        } else {
                /* cold state - try to download firmware */
-               priv->warm = 0;
 
                /* request the firmware, this will block and timeout */
                ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
index bb0c99d7a4f169d25c642b5143efcf36c087c397..b06a7e54ee0d25100f1f01abcac7ddc70e8489df 100644 (file)
@@ -628,16 +628,13 @@ static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable)
 
 static const struct v4l2_dv_timings_cap ad9389b_timings_cap = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = 1920,
-               .max_height = 1200,
-               .min_pixelclock = 25000000,
-               .max_pixelclock = 170000000,
-               .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
                        V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
-                       V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
-       },
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+               V4L2_DV_BT_CAP_CUSTOM)
 };
 
 static int ad9389b_s_dv_timings(struct v4l2_subdev *sd,
index 7a576097471f352939cc703d6fbac368cf02f532..7c8d971f1f613206b1821bfa023ea5326ba48028 100644 (file)
@@ -119,16 +119,14 @@ static int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
 
 static const struct v4l2_dv_timings_cap adv7511_timings_cap = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = ADV7511_MAX_WIDTH,
-               .max_height = ADV7511_MAX_HEIGHT,
-               .min_pixelclock = ADV7511_MIN_PIXELCLOCK,
-               .max_pixelclock = ADV7511_MAX_PIXELCLOCK,
-               .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, ADV7511_MAX_WIDTH, 0, ADV7511_MAX_HEIGHT,
+               ADV7511_MIN_PIXELCLOCK, ADV7511_MAX_PIXELCLOCK,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
                        V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
-                       V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
-       },
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+                       V4L2_DV_BT_CAP_CUSTOM)
 };
 
 static inline struct adv7511_state *get_adv7511_state(struct v4l2_subdev *sd)
@@ -1126,6 +1124,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
        state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1);
        if (state->i2c_edid == NULL) {
                v4l2_err(sd, "failed to register edid i2c client\n");
+               err = -ENOMEM;
                goto err_entity;
        }
 
@@ -1133,6 +1132,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
        state->work_queue = create_singlethread_workqueue(sd->name);
        if (state->work_queue == NULL) {
                v4l2_err(sd, "could not create workqueue\n");
+               err = -ENOMEM;
                goto err_unreg_cec;
        }
 
index d1748901337cd08be083e9a6a7d35958f6aeca7b..22f729d66a9696522e18d42932ae383594cbca5f 100644 (file)
@@ -546,30 +546,24 @@ static inline bool is_digital_input(struct v4l2_subdev *sd)
 
 static const struct v4l2_dv_timings_cap adv7842_timings_cap_analog = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = 1920,
-               .max_height = 1200,
-               .min_pixelclock = 25000000,
-               .max_pixelclock = 170000000,
-               .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
                        V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
-                       V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
-       },
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+                       V4L2_DV_BT_CAP_CUSTOM)
 };
 
 static const struct v4l2_dv_timings_cap adv7842_timings_cap_digital = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = 1920,
-               .max_height = 1200,
-               .min_pixelclock = 25000000,
-               .max_pixelclock = 225000000,
-               .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 225000000,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
                        V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
-                       V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
-       },
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+                       V4L2_DV_BT_CAP_CUSTOM)
 };
 
 static inline const struct v4l2_dv_timings_cap *
index a58a8f663ffbb3a028540fc8763ce5521e55e828..d9f65d7e3e58b64dea5b930d5800e20921fd8778 100644 (file)
@@ -46,14 +46,10 @@ struct ths8200_state {
 
 static const struct v4l2_dv_timings_cap ths8200_timings_cap = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = 1920,
-               .max_height = 1080,
-               .min_pixelclock = 25000000,
-               .max_pixelclock = 148500000,
-               .standards = V4L2_DV_BT_STD_CEA861,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE,
-       },
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1080, 25000000, 148500000,
+               V4L2_DV_BT_STD_CEA861, V4L2_DV_BT_CAP_PROGRESSIVE)
 };
 
 static inline struct ths8200_state *to_state(struct v4l2_subdev *sd)
index e12bbd8c3f0b8da63f8418e6a00bc803f883963a..fb60da85bc2c039e3ab7a6db22fa1182c4e977bb 100644 (file)
@@ -1455,6 +1455,7 @@ static int video_release(struct file *file)
 
        /* stop video capture */
        if (res_check(fh, RESOURCE_VIDEO)) {
+               pm_qos_remove_request(&dev->qos_request);
                videobuf_streamoff(&fh->cap);
                res_free(dev,fh,RESOURCE_VIDEO);
        }
index 53ad0f080179721dd97c333a051ce034d948e857..d2d3b4b614359e239a540198451fd55b81339353 100644 (file)
@@ -29,7 +29,7 @@ config VIDEO_S5P_FIMC
 config VIDEO_S5P_MIPI_CSIS
        tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
        depends on REGULATOR
-       select S5P_SETUP_MIPIPHY
+       select GENERIC_PHY
        help
          This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
          receiver (MIPI-CSIS) devices.
index 0914230b42de55ea531d63cc10bce844d1282ed3..9fc2af6a04466b402c6f931097e28dbdec2ad29c 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/memory.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_data/mipi-csis.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -180,6 +181,7 @@ struct csis_drvdata {
  * @sd: v4l2_subdev associated with CSIS device instance
  * @index: the hardware instance index
  * @pdev: CSIS platform device
+ * @phy: pointer to the CSIS generic PHY
  * @regs: mmaped I/O registers memory
  * @supplies: CSIS regulator supplies
  * @clock: CSIS clocks
@@ -203,6 +205,7 @@ struct csis_state {
        struct v4l2_subdev sd;
        u8 index;
        struct platform_device *pdev;
+       struct phy *phy;
        void __iomem *regs;
        struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
        struct clk *clock[NUM_CSIS_CLOCKS];
@@ -779,8 +782,8 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
                                        "samsung,csis-wclk");
 
        state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
-
        of_node_put(node);
+
        return 0;
 }
 #else
@@ -829,6 +832,10 @@ static int s5pcsis_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       state->phy = devm_phy_get(dev, "csis");
+       if (IS_ERR(state->phy))
+               return PTR_ERR(state->phy);
+
        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        state->regs = devm_ioremap_resource(dev, mem_res);
        if (IS_ERR(state->regs))
@@ -922,7 +929,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
        mutex_lock(&state->lock);
        if (state->flags & ST_POWERED) {
                s5pcsis_stop_stream(state);
-               ret = s5p_csis_phy_enable(state->index, false);
+               ret = phy_power_off(state->phy);
                if (ret)
                        goto unlock;
                ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
@@ -958,7 +965,7 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime)
                                            state->supplies);
                if (ret)
                        goto unlock;
-               ret = s5p_csis_phy_enable(state->index, true);
+               ret = phy_power_on(state->phy);
                if (!ret) {
                        state->flags |= ST_POWERED;
                } else {
index 15d23968d1de8392a61deb4a4bcd2b0489ce689d..9b88a46010072fbe3b4aeefa46bbc8ee0f04ea7e 100644 (file)
@@ -1423,6 +1423,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
        jpeg->vfd_decoder->release      = video_device_release;
        jpeg->vfd_decoder->lock         = &jpeg->lock;
        jpeg->vfd_decoder->v4l2_dev     = &jpeg->v4l2_dev;
+       jpeg->vfd_decoder->vfl_dir      = VFL_DIR_M2M;
 
        ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
        if (ret) {
index 7a9c5e9329f2655327c2c3d6a22e660a11728d38..4f30341dc2ab239ed4b736292c1b7ccb6d208b5e 100644 (file)
@@ -776,7 +776,7 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
        v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
                              &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
 
-       for (i = 0; ARRAY_SIZE(vou_fmt); i++)
+       for (i = 0; i < ARRAY_SIZE(vou_fmt); i++)
                if (vou_fmt[i].pfmt == pix->pixelformat)
                        return 0;
 
index 8f9f6211c52e1e052f0ce4db84b3bcd951260d72..f975b70086922c866dc415f9eca4506aef3c19ea 100644 (file)
@@ -266,7 +266,6 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
        struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
        struct idmac_video_param *video = &ichan->params.video;
        const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
-       unsigned long flags;
        dma_cookie_t cookie;
        size_t new_size;
 
@@ -328,7 +327,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
                memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
 #endif
 
-       spin_lock_irqsave(&mx3_cam->lock, flags);
+       spin_lock_irq(&mx3_cam->lock);
        list_add_tail(&buf->queue, &mx3_cam->capture);
 
        if (!mx3_cam->active)
@@ -351,7 +350,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
        if (mx3_cam->active == buf)
                mx3_cam->active = NULL;
 
-       spin_unlock_irqrestore(&mx3_cam->lock, flags);
+       spin_unlock_irq(&mx3_cam->lock);
 error:
        vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
 }
index ad9309da4a9102779f67cbd999aee72db465c51d..6c96e4898777f29f5c9ae4457d488e9424b32f2b 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include "e4000_priv.h"
+#include <linux/math64.h>
 
 /* write multiple registers */
 static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
@@ -233,7 +234,7 @@ static int e4000_set_params(struct dvb_frontend *fe)
         * or more.
         */
        f_vco = c->frequency * e4000_pll_lut[i].mul;
-       sigma_delta = 0x10000UL * (f_vco % priv->cfg->clock) / priv->cfg->clock;
+       sigma_delta = div_u64(0x10000ULL * (f_vco % priv->cfg->clock), priv->cfg->clock);
        buf[0] = f_vco / priv->cfg->clock;
        buf[1] = (sigma_delta >> 0) & 0xff;
        buf[2] = (sigma_delta >> 8) & 0xff;
index c43c8d32be40c30e99be96d6fedd5a8fdd3832d9..be77482c30704a5b81615199f563770c0147ec0f 100644 (file)
@@ -111,6 +111,13 @@ static const struct dmi_system_id stk_upside_down_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "F3JC")
                }
        },
+       {
+               .ident = "T12Rg-H",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "HCL Infosystems Limited"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "T12Rg-H")
+               }
+       },
        {}
 };
 
index 81695d48c13ebc5ceeb5560a0787aae94a516766..c3bb2502225bc6c8932d4aca36de62f1fd1daee2 100644 (file)
@@ -2090,6 +2090,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+       /* Microsoft Lifecam NX-3000 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x045e,
+         .idProduct            = 0x0721,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_DEF },
        /* Microsoft Lifecam VX-7000 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2174,6 +2183,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_DEF },
+       /* Dell SP2008WFP Monitor */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x05a9,
+         .idProduct            = 0x2641,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_DEF },
        /* Dell Alienware X51 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
index 594c75eab5a5d84b42d20ce6e5ba014fe91c0972..de0e87f0b2c3a833f113121709b9a496d29ef1f3 100644 (file)
@@ -353,7 +353,9 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 
                        if (b->m.planes[plane].bytesused > length)
                                return -EINVAL;
-                       if (b->m.planes[plane].data_offset >=
+
+                       if (b->m.planes[plane].data_offset > 0 &&
+                           b->m.planes[plane].data_offset >=
                            b->m.planes[plane].bytesused)
                                return -EINVAL;
                }
index fd56f25632018fde92f1acefc4d284e6a47d6dba..646f08f4f504c05ae37dd95cbc1fe8333705e658 100644 (file)
@@ -423,6 +423,39 @@ static inline int vma_is_io(struct vm_area_struct *vma)
        return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
 }
 
+static int vb2_dc_get_user_pfn(unsigned long start, int n_pages,
+       struct vm_area_struct *vma, unsigned long *res)
+{
+       unsigned long pfn, start_pfn, prev_pfn;
+       unsigned int i;
+       int ret;
+
+       if (!vma_is_io(vma))
+               return -EFAULT;
+
+       ret = follow_pfn(vma, start, &pfn);
+       if (ret)
+               return ret;
+
+       start_pfn = pfn;
+       start += PAGE_SIZE;
+
+       for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) {
+               prev_pfn = pfn;
+               ret = follow_pfn(vma, start, &pfn);
+
+               if (ret) {
+                       pr_err("no page for address %lu\n", start);
+                       return ret;
+               }
+               if (pfn != prev_pfn + 1)
+                       return -EINVAL;
+       }
+
+       *res = start_pfn;
+       return 0;
+}
+
 static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
        int n_pages, struct vm_area_struct *vma, int write)
 {
@@ -433,6 +466,9 @@ static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
                        unsigned long pfn;
                        int ret = follow_pfn(vma, start, &pfn);
 
+                       if (!pfn_valid(pfn))
+                               return -EINVAL;
+
                        if (ret) {
                                pr_err("no page for address %lu\n", start);
                                return ret;
@@ -468,16 +504,49 @@ static void vb2_dc_put_userptr(void *buf_priv)
        struct vb2_dc_buf *buf = buf_priv;
        struct sg_table *sgt = buf->dma_sgt;
 
-       dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
-       if (!vma_is_io(buf->vma))
-               vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
+       if (sgt) {
+               dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
+               if (!vma_is_io(buf->vma))
+                       vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
 
-       sg_free_table(sgt);
-       kfree(sgt);
+               sg_free_table(sgt);
+               kfree(sgt);
+       }
        vb2_put_vma(buf->vma);
        kfree(buf);
 }
 
+/*
+ * For some kind of reserved memory there might be no struct page available,
+ * so all that can be done to support such 'pages' is to try to convert
+ * pfn to dma address or at the last resort just assume that
+ * dma address == physical address (like it has been assumed in earlier version
+ * of videobuf2-dma-contig
+ */
+
+#ifdef __arch_pfn_to_dma
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+       return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
+}
+#elif defined(__pfn_to_bus)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+       return (dma_addr_t)__pfn_to_bus(pfn);
+}
+#elif defined(__pfn_to_phys)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+       return (dma_addr_t)__pfn_to_phys(pfn);
+}
+#else
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+       /* really, we cannot do anything better at this point */
+       return (dma_addr_t)(pfn) << PAGE_SHIFT;
+}
+#endif
+
 static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
        unsigned long size, int write)
 {
@@ -548,6 +617,14 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
        /* extract page list from userspace mapping */
        ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, write);
        if (ret) {
+               unsigned long pfn;
+               if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) {
+                       buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn);
+                       buf->size = size;
+                       kfree(pages);
+                       return buf;
+               }
+
                pr_err("failed to get user pages\n");
                goto fail_vma;
        }
index ffcb10ac434156adc89e4e9237d30e4ade530830..bbf4aea1627d389339526a5451b3ac1fc7e4815a 100644 (file)
@@ -153,24 +153,24 @@ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
        struct memstick_dev *card = container_of(dev, struct memstick_dev,    \
                                                 dev);                        \
        return sprintf(buf, format, card->id.name);                           \
-}
+}                                                                             \
+static DEVICE_ATTR_RO(name);
 
 MEMSTICK_ATTR(type, "%02X");
 MEMSTICK_ATTR(category, "%02X");
 MEMSTICK_ATTR(class, "%02X");
 
-#define MEMSTICK_ATTR_RO(name) __ATTR(name, S_IRUGO, name##_show, NULL)
-
-static struct device_attribute memstick_dev_attrs[] = {
-       MEMSTICK_ATTR_RO(type),
-       MEMSTICK_ATTR_RO(category),
-       MEMSTICK_ATTR_RO(class),
-       __ATTR_NULL
+static struct attribute *memstick_dev_attrs[] = {
+       &dev_attr_type.attr,
+       &dev_attr_category.attr,
+       &dev_attr_class.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(memstick_dev);
 
 static struct bus_type memstick_bus_type = {
        .name           = "memstick",
-       .dev_attrs      = memstick_dev_attrs,
+       .dev_groups     = memstick_dev_groups,
        .match          = memstick_bus_match,
        .uevent         = memstick_uevent,
        .probe          = memstick_device_probe,
index cbe384fb848c97078d609a751d34a8d0f627cfa7..91614f11f89a38d719b6091b9c7b8654557df3bc 100644 (file)
@@ -33,7 +33,7 @@ extern int __init i2o_pci_init(void);
 extern void __exit i2o_pci_exit(void);
 
 /* device */
-extern struct device_attribute i2o_device_attrs[];
+extern const struct attribute_group *i2o_device_groups[];
 
 extern void i2o_device_remove(struct i2o_device *);
 extern int i2o_device_parse_lct(struct i2o_controller *);
index 4547db99f7da7d6e321894419a15f5ec3c6413b8..98348f420b52b9aae3dee2c274122739fc5ef931 100644 (file)
@@ -138,45 +138,55 @@ static void i2o_device_release(struct device *dev)
 }
 
 /**
- *     i2o_device_show_class_id - Displays class id of I2O device
+ *     class_id_show - Displays class id of I2O device
  *     @dev: device of which the class id should be displayed
  *     @attr: pointer to device attribute
  *     @buf: buffer into which the class id should be printed
  *
  *     Returns the number of bytes which are printed into the buffer.
  */
-static ssize_t i2o_device_show_class_id(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
+static ssize_t class_id_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
 {
        struct i2o_device *i2o_dev = to_i2o_device(dev);
 
        sprintf(buf, "0x%03x\n", i2o_dev->lct_data.class_id);
        return strlen(buf) + 1;
 }
+static DEVICE_ATTR_RO(class_id);
 
 /**
- *     i2o_device_show_tid - Displays TID of I2O device
+ *     tid_show - Displays TID of I2O device
  *     @dev: device of which the TID should be displayed
  *     @attr: pointer to device attribute
  *     @buf: buffer into which the TID should be printed
  *
  *     Returns the number of bytes which are printed into the buffer.
  */
-static ssize_t i2o_device_show_tid(struct device *dev,
-                                  struct device_attribute *attr, char *buf)
+static ssize_t tid_show(struct device *dev, struct device_attribute *attr,
+                       char *buf)
 {
        struct i2o_device *i2o_dev = to_i2o_device(dev);
 
        sprintf(buf, "0x%03x\n", i2o_dev->lct_data.tid);
        return strlen(buf) + 1;
 }
+static DEVICE_ATTR_RO(tid);
 
 /* I2O device attributes */
-struct device_attribute i2o_device_attrs[] = {
-       __ATTR(class_id, S_IRUGO, i2o_device_show_class_id, NULL),
-       __ATTR(tid, S_IRUGO, i2o_device_show_tid, NULL),
-       __ATTR_NULL
+static struct attribute *i2o_device_attrs[] = {
+       &dev_attr_class_id.attr,
+       &dev_attr_tid.attr,
+       NULL,
+};
+
+static const struct attribute_group i2o_device_group = {
+       .attrs = i2o_device_attrs,
+};
+
+const struct attribute_group *i2o_device_groups[] = {
+       &i2o_device_group,
+       NULL,
 };
 
 /**
index 813eaa33fa1431af96a2c2f3ac40b1000dd9ed7b..b6b92d760510be0f5cd92009a616fbccc9ce088d 100644 (file)
@@ -62,7 +62,7 @@ static int i2o_bus_match(struct device *dev, struct device_driver *drv)
 struct bus_type i2o_bus_type = {
        .name = "i2o",
        .match = i2o_bus_match,
-       .dev_attrs = i2o_device_attrs
+       .dev_groups = i2o_device_groups,
 };
 
 /**
index 8dacd4c9ee8745f6449aaff8f6e9c03a4dd20341..e760715bd9cbd30c01c6c16da430c06931fb04c1 100644 (file)
@@ -537,4 +537,5 @@ source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
 source "drivers/misc/vmw_vmci/Kconfig"
+source "drivers/misc/mic/Kconfig"
 endmenu
index c235d5b683111534a39996b75d8832f063ea0ae9..0b7ea3ea8bb86cc3971c7af15258c285100bd8e1 100644 (file)
@@ -53,3 +53,4 @@ obj-$(CONFIG_INTEL_MEI)               += mei/
 obj-$(CONFIG_VMWARE_VMCI)      += vmw_vmci/
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)      += lattice-ecp3-config.o
 obj-$(CONFIG_SRAM)             += sram.o
+obj-y                          += mic/
index 1256a4bf1c04aca6465bcdde05158ade90c7881e..b7ebf8021d99d8c392d6f58961a7e3524f2d71ba 100644 (file)
@@ -297,7 +297,7 @@ static int __init charlcd_probe(struct platform_device *pdev)
        lcd->irq = platform_get_irq(pdev, 0);
        /* If no IRQ is supplied, we'll survive without it */
        if (lcd->irq >= 0) {
-               if (request_irq(lcd->irq, charlcd_interrupt, IRQF_DISABLED,
+               if (request_irq(lcd->irq, charlcd_interrupt, 0,
                                DRIVERNAME, lcd)) {
                        ret = -EIO;
                        goto out_no_irq;
index 494d0500bda6483a9f3d93f6287075d5f0825448..a6dc56e1bc58e70566bd6321bca1ae1191519b3b 100644 (file)
@@ -90,8 +90,10 @@ int pwm_channel_alloc(int index, struct pwm_channel *ch)
        unsigned long   flags;
        int             status = 0;
 
-       /* insist on PWM init, with this signal pinned out */
-       if (!pwm || !(pwm->mask & 1 << index))
+       if (!pwm)
+               return -EPROBE_DEFER;
+
+       if (!(pwm->mask & 1 << index))
                return -ENODEV;
 
        if (index < 0 || index >= PWM_NCHAN || !ch)
index 057580e026c056f7b6ff576d51fbe225db0efd47..48ea33d15a79eca71bce4c7f3c986d7609880304 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/module.h>
+#include <linux/of.h>
 
 #define BH1780_REG_CONTROL     0x80
 #define BH1780_REG_PARTID      0x8A
@@ -244,6 +245,15 @@ static const struct i2c_device_id bh1780_id[] = {
        { },
 };
 
+#ifdef CONFIG_OF
+static const struct of_device_id of_bh1780_match[] = {
+       { .compatible = "rohm,bh1780gli", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, of_bh1780_match);
+#endif
+
 static struct i2c_driver bh1780_driver = {
        .probe          = bh1780_probe,
        .remove         = bh1780_remove,
@@ -251,6 +261,7 @@ static struct i2c_driver bh1780_driver = {
        .driver = {
                .name = "bh1780",
                .pm     = &bh1780_pm,
+               .of_match_table = of_match_ptr(of_bh1780_match),
        },
 };
 
index 849e2fed4da2841310ee9a5cd80430cb29f6d950..2704d885a9b30517cea14c91fda89d5e6748a64e 100644 (file)
@@ -374,7 +374,7 @@ int bmp085_detect(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(bmp085_detect);
 
-static void __init bmp085_get_of_properties(struct bmp085_data *data)
+static void bmp085_get_of_properties(struct bmp085_data *data)
 {
 #ifdef CONFIG_OF
        struct device_node *np = data->dev->of_node;
index 2e50f811ff599205aade6f1b877bc7877e3917bb..fb397e7d1cce8a17ab2583c2bedc49438d72b03b 100644 (file)
@@ -176,7 +176,7 @@ static int cb710_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct cb710_chip *chip = pci_get_drvdata(pdev);
 
-       free_irq(pdev->irq, chip);
+       devm_free_irq(&pdev->dev, pdev->irq, chip);
        pci_save_state(pdev);
        pci_disable_device(pdev);
        if (state.event & PM_EVENT_SLEEP)
index 04f2e1fa9dd15b10871919a788c81aff782178f8..9536852fd4c68e28673b6325737fdf1cd82df5c1 100644 (file)
@@ -96,4 +96,17 @@ config EEPROM_DIGSY_MTC_CFG
 
          If unsure, say N.
 
+config EEPROM_SUNXI_SID
+       tristate "Allwinner sunxi security ID support"
+       depends on ARCH_SUNXI && SYSFS
+       help
+         This is a driver for the 'security ID' available on various Allwinner
+         devices.
+
+         Due to the potential risks involved with changing e-fuses,
+         this driver is read-only.
+
+         This driver can also be built as a module. If so, the module
+         will be called sunxi_sid.
+
 endmenu
index fc1e81d292673b14732e9268b7cb9cdca9d921fb..9507aec95e948f88f31579585e60b8632c0b3782 100644 (file)
@@ -4,4 +4,5 @@ obj-$(CONFIG_EEPROM_LEGACY)     += eeprom.o
 obj-$(CONFIG_EEPROM_MAX6875)   += max6875.o
 obj-$(CONFIG_EEPROM_93CX6)     += eeprom_93cx6.o
 obj-$(CONFIG_EEPROM_93XX46)    += eeprom_93xx46.o
+obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o
 obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
index 5d4fd69d04ca05f1b189e458fdbacd4e54e3a0b7..94b8a33243192d4217c54442424bc91e1dbd936c 100644 (file)
@@ -428,6 +428,9 @@ static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
 {
        struct at24_data *at24;
 
+       if (unlikely(off >= attr->size))
+               return -EFBIG;
+
        at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
        return at24_write(at24, buf, off, count);
 }
index 840b3594a5ae3c852b1e643a9f4b7e167033ad4f..4f3bca1003a175d2eb0ea7fdf423cd8dfa70e14d 100644 (file)
@@ -462,10 +462,17 @@ static int at25_remove(struct spi_device *spi)
 
 /*-------------------------------------------------------------------------*/
 
+static const struct of_device_id at25_of_match[] = {
+       { .compatible = "atmel,at25", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, at25_of_match);
+
 static struct spi_driver at25_driver = {
        .driver = {
                .name           = "at25",
                .owner          = THIS_MODULE,
+               .of_match_table = at25_of_match,
        },
        .probe          = at25_probe,
        .remove         = at25_remove,
index 94cfc121257710459b98a5088d1820d618080e10..3a015abb444a826baecef6c6596f6a5c58adb648 100644 (file)
@@ -202,7 +202,7 @@ eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
        edev = dev_get_drvdata(dev);
 
        if (unlikely(off >= edev->bin.size))
-               return 0;
+               return -EFBIG;
        if ((off + count) > edev->bin.size)
                count = edev->bin.size - off;
        if (unlikely(!count))
diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c
new file mode 100644 (file)
index 0000000..9c34e57
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
+ * http://www.linux-sunxi.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver exposes the Allwinner security ID, efuses exported in byte-
+ * sized chunks.
+ */
+
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#define DRV_NAME "sunxi-sid"
+
+struct sunxi_sid_data {
+       void __iomem *reg_base;
+       unsigned int keysize;
+};
+
+/* We read the entire key, due to a 32 bit read alignment requirement. Since we
+ * want to return the requested byte, this results in somewhat slower code and
+ * uses 4 times more reads as needed but keeps code simpler. Since the SID is
+ * only very rarely probed, this is not really an issue.
+ */
+static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data,
+                             const unsigned int offset)
+{
+       u32 sid_key;
+
+       if (offset >= sid_data->keysize)
+               return 0;
+
+       sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4));
+       sid_key >>= (offset % 4) * 8;
+
+       return sid_key; /* Only return the last byte */
+}
+
+static ssize_t sid_read(struct file *fd, struct kobject *kobj,
+                       struct bin_attribute *attr, char *buf,
+                       loff_t pos, size_t size)
+{
+       struct platform_device *pdev;
+       struct sunxi_sid_data *sid_data;
+       int i;
+
+       pdev = to_platform_device(kobj_to_dev(kobj));
+       sid_data = platform_get_drvdata(pdev);
+
+       if (pos < 0 || pos >= sid_data->keysize)
+               return 0;
+       if (size > sid_data->keysize - pos)
+               size = sid_data->keysize - pos;
+
+       for (i = 0; i < size; i++)
+               buf[i] = sunxi_sid_read_byte(sid_data, pos + i);
+
+       return i;
+}
+
+static struct bin_attribute sid_bin_attr = {
+       .attr = { .name = "eeprom", .mode = S_IRUGO, },
+       .read = sid_read,
+};
+
+static int sunxi_sid_remove(struct platform_device *pdev)
+{
+       device_remove_bin_file(&pdev->dev, &sid_bin_attr);
+       dev_dbg(&pdev->dev, "driver unloaded\n");
+
+       return 0;
+}
+
+static const struct of_device_id sunxi_sid_of_match[] = {
+       { .compatible = "allwinner,sun4i-sid", .data = (void *)16},
+       { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
+       {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
+
+static int sunxi_sid_probe(struct platform_device *pdev)
+{
+       struct sunxi_sid_data *sid_data;
+       struct resource *res;
+       const struct of_device_id *of_dev_id;
+       u8 *entropy;
+       unsigned int i;
+
+       sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data),
+                               GFP_KERNEL);
+       if (!sid_data)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(sid_data->reg_base))
+               return PTR_ERR(sid_data->reg_base);
+
+       of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev);
+       if (!of_dev_id)
+               return -ENODEV;
+       sid_data->keysize = (int)of_dev_id->data;
+
+       platform_set_drvdata(pdev, sid_data);
+
+       sid_bin_attr.size = sid_data->keysize;
+       if (device_create_bin_file(&pdev->dev, &sid_bin_attr))
+               return -ENODEV;
+
+       entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL);
+       for (i = 0; i < sid_data->keysize; i++)
+               entropy[i] = sunxi_sid_read_byte(sid_data, i);
+       add_device_randomness(entropy, sid_data->keysize);
+       kfree(entropy);
+
+       dev_dbg(&pdev->dev, "loaded\n");
+
+       return 0;
+}
+
+static struct platform_driver sunxi_sid_driver = {
+       .probe = sunxi_sid_probe,
+       .remove = sunxi_sid_remove,
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = sunxi_sid_of_match,
+       },
+};
+module_platform_driver(sunxi_sid_driver);
+
+MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
+MODULE_DESCRIPTION("Allwinner sunxi security id driver");
+MODULE_LICENSE("GPL");
index 0346d87c5fed9f20fffa622269b4102bbdeb516f..6b3bf9ab051db1e5e0c1ef15dec4bbd50906da42 100644 (file)
@@ -153,7 +153,6 @@ error_ioremap:
 error_heartbeat:
        ibmasm_event_buffer_exit(sp);
 error_eventbuffer:
-       pci_set_drvdata(pdev, NULL);
        kfree(sp);
 error_kmalloc:
         pci_release_regions(pdev);
@@ -165,7 +164,7 @@ error_resources:
 
 static void ibmasm_remove_one(struct pci_dev *pdev)
 {
-       struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev);
+       struct service_processor *sp = pci_get_drvdata(pdev);
 
        dbg("Unregistering UART\n");
        ibmasm_unregister_uart(sp);
@@ -182,7 +181,6 @@ static void ibmasm_remove_one(struct pci_dev *pdev)
        ibmasm_free_remote_input_dev(sp);
        iounmap(sp->base_address);
        ibmasm_event_buffer_exit(sp);
-       pci_set_drvdata(pdev, NULL);
        kfree(sp);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
index 2fc0586ce3bb772b7f3100e983ecdd21b43f08bd..a2edb2ee0921657e188b89311673793bb2aa5fff 100644 (file)
 #include <scsi/scsi_cmnd.h>
 #include <linux/debugfs.h>
 #include <linux/vmalloc.h>
+#include <linux/mman.h>
 
 #ifdef CONFIG_IDE
 #include <linux/ide.h>
 #endif
 
+/*
+ * Make sure our attempts to over run the kernel stack doesn't trigger
+ * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we
+ * recurse past the end of THREAD_SIZE by default.
+ */
+#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0)
+#define REC_STACK_SIZE (CONFIG_FRAME_WARN / 2)
+#else
+#define REC_STACK_SIZE (THREAD_SIZE / 8)
+#endif
+#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2)
+
 #define DEFAULT_COUNT 10
-#define REC_NUM_DEFAULT 10
 #define EXEC_SIZE 64
 
 enum cname {
@@ -86,6 +98,9 @@ enum ctype {
        CT_EXEC_STACK,
        CT_EXEC_KMALLOC,
        CT_EXEC_VMALLOC,
+       CT_EXEC_USERSPACE,
+       CT_ACCESS_USERSPACE,
+       CT_WRITE_RO,
 };
 
 static char* cp_name[] = {
@@ -119,6 +134,9 @@ static char* cp_type[] = {
        "EXEC_STACK",
        "EXEC_KMALLOC",
        "EXEC_VMALLOC",
+       "EXEC_USERSPACE",
+       "ACCESS_USERSPACE",
+       "WRITE_RO",
 };
 
 static struct jprobe lkdtm;
@@ -139,9 +157,10 @@ static DEFINE_SPINLOCK(lock_me_up);
 
 static u8 data_area[EXEC_SIZE];
 
+static const unsigned long rodata = 0xAA55AA55;
+
 module_param(recur_count, int, 0644);
-MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\
-                                "default is 10");
+MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test");
 module_param(cpoint_name, charp, 0444);
 MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed");
 module_param(cpoint_type, charp, 0444);
@@ -280,16 +299,16 @@ static int lkdtm_parse_commandline(void)
        return -EINVAL;
 }
 
-static int recursive_loop(int a)
+static int recursive_loop(int remaining)
 {
-       char buf[1024];
+       char buf[REC_STACK_SIZE];
 
-       memset(buf,0xFF,1024);
-       recur_count--;
-       if (!recur_count)
+       /* Make sure compiler does not optimize this away. */
+       memset(buf, (remaining & 0xff) | 0x1, REC_STACK_SIZE);
+       if (!remaining)
                return 0;
        else
-               return recursive_loop(a);
+               return recursive_loop(remaining - 1);
 }
 
 static void do_nothing(void)
@@ -297,6 +316,14 @@ static void do_nothing(void)
        return;
 }
 
+static noinline void corrupt_stack(void)
+{
+       /* Use default char array length that triggers stack protection. */
+       char data[8];
+
+       memset((void *)data, 0, 64);
+}
+
 static void execute_location(void *dst)
 {
        void (*func)(void) = dst;
@@ -305,6 +332,15 @@ static void execute_location(void *dst)
        func();
 }
 
+static void execute_user_location(void *dst)
+{
+       void (*func)(void) = dst;
+
+       if (copy_to_user(dst, do_nothing, EXEC_SIZE))
+               return;
+       func();
+}
+
 static void lkdtm_do_action(enum ctype which)
 {
        switch (which) {
@@ -325,15 +361,11 @@ static void lkdtm_do_action(enum ctype which)
                        ;
                break;
        case CT_OVERFLOW:
-               (void) recursive_loop(0);
+               (void) recursive_loop(recur_count);
                break;
-       case CT_CORRUPT_STACK: {
-               /* Make sure the compiler creates and uses an 8 char array. */
-               volatile char data[8];
-
-               memset((void *)data, 0, 64);
+       case CT_CORRUPT_STACK:
+               corrupt_stack();
                break;
-       }
        case CT_UNALIGNED_LOAD_STORE_WRITE: {
                static u8 data[5] __attribute__((aligned(4))) = {1, 2,
                                3, 4, 5};
@@ -401,6 +433,49 @@ static void lkdtm_do_action(enum ctype which)
                vfree(vmalloc_area);
                break;
        }
+       case CT_EXEC_USERSPACE: {
+               unsigned long user_addr;
+
+               user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
+                                   PROT_READ | PROT_WRITE | PROT_EXEC,
+                                   MAP_ANONYMOUS | MAP_PRIVATE, 0);
+               if (user_addr >= TASK_SIZE) {
+                       pr_warn("Failed to allocate user memory\n");
+                       return;
+               }
+               execute_user_location((void *)user_addr);
+               vm_munmap(user_addr, PAGE_SIZE);
+               break;
+       }
+       case CT_ACCESS_USERSPACE: {
+               unsigned long user_addr, tmp;
+               unsigned long *ptr;
+
+               user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
+                                   PROT_READ | PROT_WRITE | PROT_EXEC,
+                                   MAP_ANONYMOUS | MAP_PRIVATE, 0);
+               if (user_addr >= TASK_SIZE) {
+                       pr_warn("Failed to allocate user memory\n");
+                       return;
+               }
+
+               ptr = (unsigned long *)user_addr;
+               tmp = *ptr;
+               tmp += 0xc0dec0de;
+               *ptr = tmp;
+
+               vm_munmap(user_addr, PAGE_SIZE);
+
+               break;
+       }
+       case CT_WRITE_RO: {
+               unsigned long *ptr;
+
+               ptr = (unsigned long *)&rodata;
+               *ptr ^= 0xabcd1234;
+
+               break;
+       }
        case CT_NONE:
        default:
                break;
index f6ff711aa5bbbaa4a177dbd416ccdab53017bb23..d22c6864508b2de3505f56fa7c0a44b97dbe90fb 100644 (file)
@@ -58,6 +58,7 @@ void mei_amthif_reset_params(struct mei_device *dev)
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
        dev->iamthif_timer = 0;
        dev->iamthif_stall_timer = 0;
+       dev->iamthif_open_count = 0;
 }
 
 /**
@@ -78,8 +79,10 @@ int mei_amthif_host_init(struct mei_device *dev)
 
        i = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
        if (i < 0) {
-               dev_info(&dev->pdev->dev, "amthif: failed to find the client\n");
-               return -ENOENT;
+               ret = i;
+               dev_info(&dev->pdev->dev,
+                       "amthif: failed to find the client %d\n", ret);
+               return ret;
        }
 
        cl->me_client_id = dev->me_clients[i].client_id;
@@ -106,8 +109,9 @@ int mei_amthif_host_init(struct mei_device *dev)
        ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
 
        if (ret < 0) {
-               dev_err(&dev->pdev->dev, "amthif: failed link client\n");
-               return -ENOENT;
+               dev_err(&dev->pdev->dev,
+                       "amthif: failed link client %d\n", ret);
+               return ret;
        }
 
        cl->state = MEI_FILE_CONNECTING;
@@ -313,13 +317,13 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
                mei_hdr.me_addr = dev->iamthif_cl.me_client_id;
                mei_hdr.reserved = 0;
                dev->iamthif_msg_buf_index += mei_hdr.length;
-               if (mei_write_message(dev, &mei_hdr,
-                                       (unsigned char *)dev->iamthif_msg_buf))
-                       return -ENODEV;
+               ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf);
+               if (ret)
+                       return ret;
 
                if (mei_hdr.msg_complete) {
                        if (mei_cl_flow_ctrl_reduce(&dev->iamthif_cl))
-                               return -ENODEV;
+                               return -EIO;
                        dev->iamthif_flow_control_pending = true;
                        dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
                        dev_dbg(&dev->pdev->dev, "add amthif cb to write waiting list\n");
@@ -459,6 +463,16 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
        struct mei_msg_hdr mei_hdr;
        size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index;
        u32 msg_slots = mei_data2slots(len);
+       int rets;
+
+       rets = mei_cl_flow_ctrl_creds(cl);
+       if (rets < 0)
+               return rets;
+
+       if (rets == 0) {
+               cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
+               return 0;
+       }
 
        mei_hdr.host_addr = cl->host_client_id;
        mei_hdr.me_addr = cl->me_client_id;
@@ -481,16 +495,17 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
        dev_dbg(&dev->pdev->dev, MEI_HDR_FMT,  MEI_HDR_PRM(&mei_hdr));
 
        *slots -=  msg_slots;
-       if (mei_write_message(dev, &mei_hdr,
-               dev->iamthif_msg_buf + dev->iamthif_msg_buf_index)) {
-                       dev->iamthif_state = MEI_IAMTHIF_IDLE;
-                       cl->status = -ENODEV;
-                       list_del(&cb->list);
-                       return -ENODEV;
+       rets = mei_write_message(dev, &mei_hdr,
+                       dev->iamthif_msg_buf + dev->iamthif_msg_buf_index);
+       if (rets) {
+               dev->iamthif_state = MEI_IAMTHIF_IDLE;
+               cl->status = rets;
+               list_del(&cb->list);
+               return rets;
        }
 
        if (mei_cl_flow_ctrl_reduce(cl))
-               return -ENODEV;
+               return -EIO;
 
        dev->iamthif_msg_buf_index += mei_hdr.length;
        cl->status = 0;
@@ -720,8 +735,8 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file)
 */
 int mei_amthif_release(struct mei_device *dev, struct file *file)
 {
-       if (dev->open_handle_count > 0)
-               dev->open_handle_count--;
+       if (dev->iamthif_open_count > 0)
+               dev->iamthif_open_count--;
 
        if (dev->iamthif_file_object == file &&
            dev->iamthif_state != MEI_IAMTHIF_IDLE) {
index cd2033cd7120d7631fa8d345409ed9ea86259799..4bc7d620d695f05c26a558cfe70992a4dae83963 100644 (file)
@@ -245,7 +245,7 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
        /* Check if we have an ME client device */
        id = mei_me_cl_by_id(dev, cl->me_client_id);
        if (id < 0)
-               return -ENODEV;
+               return id;
 
        if (length > dev->me_clients[id].props.max_msg_length)
                return -EINVAL;
index e0684b4d9a08aebd26453de23c5b88fb69b6b7d7..87c96e4669e2c50fd9bb5889c813f3a35ce64b1f 100644 (file)
@@ -187,10 +187,14 @@ int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length)
  */
 int mei_cl_flush_queues(struct mei_cl *cl)
 {
+       struct mei_device *dev;
+
        if (WARN_ON(!cl || !cl->dev))
                return -EINVAL;
 
-       dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n");
+       dev = cl->dev;
+
+       cl_dbg(dev, cl, "remove list entry belonging to cl\n");
        mei_io_list_flush(&cl->dev->read_list, cl);
        mei_io_list_flush(&cl->dev->write_list, cl);
        mei_io_list_flush(&cl->dev->write_waiting_list, cl);
@@ -271,6 +275,7 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
 int mei_cl_link(struct mei_cl *cl, int id)
 {
        struct mei_device *dev;
+       long open_handle_count;
 
        if (WARN_ON(!cl || !cl->dev))
                return -EINVAL;
@@ -284,7 +289,14 @@ int mei_cl_link(struct mei_cl *cl, int id)
 
        if (id >= MEI_CLIENTS_MAX) {
                dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ;
-               return -ENOENT;
+               return -EMFILE;
+       }
+
+       open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
+       if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
+               dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
+                       MEI_MAX_OPEN_HANDLE_COUNT);
+               return -EMFILE;
        }
 
        dev->open_handle_count++;
@@ -296,7 +308,7 @@ int mei_cl_link(struct mei_cl *cl, int id)
 
        cl->state = MEI_FILE_INITIALIZING;
 
-       dev_dbg(&dev->pdev->dev, "link cl host id = %d\n", cl->host_client_id);
+       cl_dbg(dev, cl, "link cl\n");
        return 0;
 }
 
@@ -308,7 +320,6 @@ int mei_cl_link(struct mei_cl *cl, int id)
 int mei_cl_unlink(struct mei_cl *cl)
 {
        struct mei_device *dev;
-       struct mei_cl *pos, *next;
 
        /* don't shout on error exit path */
        if (!cl)
@@ -320,14 +331,21 @@ int mei_cl_unlink(struct mei_cl *cl)
 
        dev = cl->dev;
 
-       list_for_each_entry_safe(pos, next, &dev->file_list, link) {
-               if (cl->host_client_id == pos->host_client_id) {
-                       dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
-                               pos->host_client_id, pos->me_client_id);
-                       list_del_init(&pos->link);
-                       break;
-               }
-       }
+       cl_dbg(dev, cl, "unlink client");
+
+       if (dev->open_handle_count > 0)
+               dev->open_handle_count--;
+
+       /* never clear the 0 bit */
+       if (cl->host_client_id)
+               clear_bit(cl->host_client_id, dev->host_clients_map);
+
+       list_del_init(&cl->link);
+
+       cl->state = MEI_FILE_INITIALIZING;
+
+       list_del_init(&cl->link);
+
        return 0;
 }
 
@@ -341,17 +359,6 @@ void mei_host_client_init(struct work_struct *work)
 
        mutex_lock(&dev->device_lock);
 
-       bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
-       dev->open_handle_count = 0;
-
-       /*
-        * Reserving the first three client IDs
-        * 0: Reserved for MEI Bus Message communications
-        * 1: Reserved for Watchdog
-        * 2: Reserved for AMTHI
-        */
-       bitmap_set(dev->host_clients_map, 0, 3);
-
        for (i = 0; i < dev->me_clients_num; i++) {
                client_props = &dev->me_clients[i].props;
 
@@ -390,6 +397,8 @@ int mei_cl_disconnect(struct mei_cl *cl)
 
        dev = cl->dev;
 
+       cl_dbg(dev, cl, "disconnecting");
+
        if (cl->state != MEI_FILE_DISCONNECTING)
                return 0;
 
@@ -402,13 +411,13 @@ int mei_cl_disconnect(struct mei_cl *cl)
                dev->hbuf_is_ready = false;
                if (mei_hbm_cl_disconnect_req(dev, cl)) {
                        rets = -ENODEV;
-                       dev_err(&dev->pdev->dev, "failed to disconnect.\n");
+                       cl_err(dev, cl, "failed to disconnect.\n");
                        goto free;
                }
                mdelay(10); /* Wait for hardware disconnection ready */
                list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
        } else {
-               dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
+               cl_dbg(dev, cl, "add disconnect cb to control write list\n");
                list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
 
        }
@@ -421,18 +430,17 @@ int mei_cl_disconnect(struct mei_cl *cl)
        mutex_lock(&dev->device_lock);
        if (MEI_FILE_DISCONNECTED == cl->state) {
                rets = 0;
-               dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n");
+               cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
        } else {
                rets = -ENODEV;
                if (MEI_FILE_DISCONNECTED != cl->state)
-                       dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n");
+                       cl_err(dev, cl, "wrong status client disconnect.\n");
 
                if (err)
-                       dev_dbg(&dev->pdev->dev,
-                                       "wait failed disconnect err=%08x\n",
+                       cl_dbg(dev, cl, "wait failed disconnect err=%08x\n",
                                        err);
 
-               dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n");
+               cl_err(dev, cl, "failed to disconnect from FW client.\n");
        }
 
        mei_io_list_flush(&dev->ctrl_rd_list, cl);
@@ -639,13 +647,12 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
                return -ENODEV;
 
        if (cl->read_cb) {
-               dev_dbg(&dev->pdev->dev, "read is pending.\n");
+               cl_dbg(dev, cl, "read is pending.\n");
                return -EBUSY;
        }
        i = mei_me_cl_by_id(dev, cl->me_client_id);
        if (i < 0) {
-               dev_err(&dev->pdev->dev, "no such me client %d\n",
-                       cl->me_client_id);
+               cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
                return  -ENODEV;
        }
 
@@ -664,6 +671,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
        if (dev->hbuf_is_ready) {
                dev->hbuf_is_ready = false;
                if (mei_hbm_cl_flow_control_req(dev, cl)) {
+                       cl_err(dev, cl, "flow control send failed\n");
                        rets = -ENODEV;
                        goto err;
                }
@@ -691,10 +699,32 @@ err:
 int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
                                     s32 *slots, struct mei_cl_cb *cmpl_list)
 {
-       struct mei_device *dev = cl->dev;
+       struct mei_device *dev;
+       struct mei_msg_data *buf;
        struct mei_msg_hdr mei_hdr;
-       size_t len = cb->request_buffer.size - cb->buf_idx;
-       u32 msg_slots = mei_data2slots(len);
+       size_t len;
+       u32 msg_slots;
+       int rets;
+
+
+       if (WARN_ON(!cl || !cl->dev))
+               return -ENODEV;
+
+       dev = cl->dev;
+
+       buf = &cb->request_buffer;
+
+       rets = mei_cl_flow_ctrl_creds(cl);
+       if (rets < 0)
+               return rets;
+
+       if (rets == 0) {
+               cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
+               return 0;
+       }
+
+       len = buf->size - cb->buf_idx;
+       msg_slots = mei_data2slots(len);
 
        mei_hdr.host_addr = cl->host_client_id;
        mei_hdr.me_addr = cl->me_client_id;
@@ -714,16 +744,15 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
                return 0;
        }
 
-       dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
+       cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
                        cb->request_buffer.size, cb->buf_idx);
-       dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
 
        *slots -=  msg_slots;
-       if (mei_write_message(dev, &mei_hdr,
-                       cb->request_buffer.data + cb->buf_idx)) {
-               cl->status = -ENODEV;
+       rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
+       if (rets) {
+               cl->status = rets;
                list_move_tail(&cb->list, &cmpl_list->list);
-               return -ENODEV;
+               return rets;
        }
 
        cl->status = 0;
@@ -732,7 +761,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
 
        if (mei_hdr.msg_complete) {
                if (mei_cl_flow_ctrl_reduce(cl))
-                       return -ENODEV;
+                       return -EIO;
                list_move_tail(&cb->list, &dev->write_waiting_list.list);
        }
 
@@ -767,7 +796,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
 
        buf = &cb->request_buffer;
 
-       dev_dbg(&dev->pdev->dev, "mei_cl_write %d\n", buf->size);
+       cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size);
 
 
        cb->fop_type = MEI_FOP_WRITE;
@@ -800,14 +829,10 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
        mei_hdr.me_addr = cl->me_client_id;
        mei_hdr.reserved = 0;
 
-       dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n",
-               MEI_HDR_PRM(&mei_hdr));
-
 
-       if (mei_write_message(dev, &mei_hdr, buf->data)) {
-               rets = -EIO;
+       rets = mei_write_message(dev, &mei_hdr, buf->data);
+       if (rets)
                goto err;
-       }
 
        cl->writing_state = MEI_WRITING;
        cb->buf_idx = mei_hdr.length;
@@ -898,11 +923,11 @@ void mei_cl_all_wakeup(struct mei_device *dev)
        struct mei_cl *cl, *next;
        list_for_each_entry_safe(cl, next, &dev->file_list, link) {
                if (waitqueue_active(&cl->rx_wait)) {
-                       dev_dbg(&dev->pdev->dev, "Waking up reading client!\n");
+                       cl_dbg(dev, cl, "Waking up reading client!\n");
                        wake_up_interruptible(&cl->rx_wait);
                }
                if (waitqueue_active(&cl->tx_wait)) {
-                       dev_dbg(&dev->pdev->dev, "Waking up writing client!\n");
+                       cl_dbg(dev, cl, "Waking up writing client!\n");
                        wake_up_interruptible(&cl->tx_wait);
                }
        }
index 892cc4207fa202629e27698c0ae536f0b81880b0..c8396e582f1c697d296834cb62f19442998b59be 100644 (file)
@@ -115,4 +115,13 @@ void mei_cl_all_disconnect(struct mei_device *dev);
 void mei_cl_all_wakeup(struct mei_device *dev);
 void mei_cl_all_write_clear(struct mei_device *dev);
 
+#define MEI_CL_FMT "cl:host=%02d me=%02d "
+#define MEI_CL_PRM(cl) (cl)->host_client_id, (cl)->me_client_id
+
+#define cl_dbg(dev, cl, format, arg...) \
+       dev_dbg(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg)
+
+#define cl_err(dev, cl, format, arg...) \
+       dev_err(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg)
+
 #endif /* _MEI_CLIENT_H_ */
index 0a0448326e9d583f951932b220d90186c59f92b7..9b3a0fb7f265861bc23e42797281e5d2423bc5fc 100644 (file)
@@ -49,7 +49,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)
        kfree(dev->me_clients);
        dev->me_clients = NULL;
 
-       dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n",
+       dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
                dev->me_clients_num * sizeof(struct mei_me_client));
        /* allocate storage for ME clients representation */
        clients = kcalloc(dev->me_clients_num,
@@ -174,7 +174,7 @@ int mei_hbm_start_req(struct mei_device *dev)
                dev_err(&dev->pdev->dev, "version message write failed\n");
                dev->dev_state = MEI_DEV_RESETTING;
                mei_reset(dev, 1);
-               return -ENODEV;
+               return -EIO;
        }
        dev->hbm_state = MEI_HBM_START;
        dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
@@ -677,7 +677,10 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
 
        case HOST_ENUM_RES_CMD:
                enum_res = (struct hbm_host_enum_response *) mei_msg;
-               memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
+               BUILD_BUG_ON(sizeof(dev->me_clients_map)
+                               < sizeof(enum_res->valid_addresses));
+               memcpy(dev->me_clients_map, enum_res->valid_addresses,
+                       sizeof(enum_res->valid_addresses));
                if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
                    dev->hbm_state == MEI_HBM_ENUM_CLIENTS) {
                                dev->init_clients_timer = 0;
index 6a203b6e83463e6252cdb6123780b1c5de63c8f0..6c0fde55270d3681c6048c1516b6915c291e4c45 100644 (file)
 #define MEI_DEV_ID_PPT_3      0x1DBA  /* Panther Point */
 
 #define MEI_DEV_ID_LPT        0x8C3A  /* Lynx Point */
+#define MEI_DEV_ID_LPT_W      0x8D3A  /* Lynx Point - Wellsburg */
 #define MEI_DEV_ID_LPT_LP     0x9C3A  /* Lynx Point LP */
 /*
  * MEI HW Section
index 6197018e2f16a24296619442fe6c8744c7c55b88..f7f3abbe12b6e2a8cc7058397ce77dc0c3cc7029 100644 (file)
@@ -68,6 +68,14 @@ void mei_device_init(struct mei_device *dev)
        mei_io_list_init(&dev->amthif_cmd_list);
        mei_io_list_init(&dev->amthif_rd_complete_list);
 
+       bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
+       dev->open_handle_count = 0;
+
+       /*
+        * Reserving the first client ID
+        * 0: Reserved for MEI Bus Message communications
+        */
+       bitmap_set(dev->host_clients_map, 0, 1);
 }
 EXPORT_SYMBOL_GPL(mei_device_init);
 
@@ -139,6 +147,10 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
                        dev->dev_state != MEI_DEV_POWER_DOWN &&
                        dev->dev_state != MEI_DEV_POWER_UP);
 
+       if (unexpected)
+               dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
+                        mei_dev_state_str(dev->dev_state));
+
        ret = mei_hw_reset(dev, interrupts_enabled);
        if (ret) {
                dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n");
@@ -165,12 +177,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
                /* remove entry if already in list */
                dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
                mei_cl_unlink(&dev->wd_cl);
-               if (dev->open_handle_count > 0)
-                       dev->open_handle_count--;
                mei_cl_unlink(&dev->iamthif_cl);
-               if (dev->open_handle_count > 0)
-                       dev->open_handle_count--;
-
                mei_amthif_reset_params(dev);
                memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
        }
@@ -182,10 +189,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
        dev->rd_msg_hdr = 0;
        dev->wd_pending = false;
 
-       if (unexpected)
-               dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
-                        mei_dev_state_str(dev->dev_state));
-
        if (!interrupts_enabled) {
                dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n");
                return;
index 4b59cb742dee9bac451b4cbf5d268083c8534ea7..7a95c07e59a6d675a4cc37a41c70fbd3e95961d7 100644 (file)
@@ -113,13 +113,13 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
 
                if (cb->response_buffer.size == 0 ||
                    cb->response_buffer.data == NULL) {
-                       dev_err(&dev->pdev->dev, "response buffer is not allocated.\n");
+                       cl_err(dev, cl, "response buffer is not allocated.\n");
                        list_del(&cb->list);
                        return -ENOMEM;
                }
 
                if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) {
-                       dev_dbg(&dev->pdev->dev, "message overflow. size %d len %d idx %ld\n",
+                       cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n",
                                cb->response_buffer.size,
                                mei_hdr->length, cb->buf_idx);
                        buffer = krealloc(cb->response_buffer.data,
@@ -127,7 +127,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
                                          GFP_KERNEL);
 
                        if (!buffer) {
-                               dev_err(&dev->pdev->dev, "allocation failed.\n");
+                               cl_err(dev, cl, "allocation failed.\n");
                                list_del(&cb->list);
                                return -ENOMEM;
                        }
@@ -143,9 +143,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
                if (mei_hdr->msg_complete) {
                        cl->status = 0;
                        list_del(&cb->list);
-                       dev_dbg(&dev->pdev->dev, "completed read H cl = %d, ME cl = %d, length = %lu\n",
-                               cl->host_client_id,
-                               cl->me_client_id,
+                       cl_dbg(dev, cl, "completed read length = %lu\n",
                                cb->buf_idx);
                        list_add_tail(&cb->list, &complete_list->list);
                }
@@ -218,9 +216,11 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
                           s32 *slots, struct mei_cl_cb *cmpl_list)
 {
        struct mei_device *dev = cl->dev;
-
        u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
 
+       int ret;
+
+
        if (*slots < msg_slots) {
                /* return the cancel routine */
                list_del(&cb->list);
@@ -229,12 +229,14 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
 
        *slots -= msg_slots;
 
-       if (mei_hbm_cl_flow_control_req(dev, cl)) {
-               cl->status = -ENODEV;
+       ret = mei_hbm_cl_flow_control_req(dev, cl);
+       if (ret) {
+               cl->status = ret;
                cb->buf_idx = 0;
                list_move_tail(&cb->list, &cmpl_list->list);
-               return -ENODEV;
+               return ret;
        }
+
        list_move_tail(&cb->list, &dev->read_list.list);
 
        return 0;
@@ -256,6 +258,7 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb,
                           s32 *slots, struct mei_cl_cb *cmpl_list)
 {
        struct mei_device *dev = cl->dev;
+       int ret;
 
        u32 msg_slots =
                mei_data2slots(sizeof(struct hbm_client_connect_request));
@@ -270,11 +273,12 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb,
 
        cl->state = MEI_FILE_CONNECTING;
 
-       if (mei_hbm_cl_connect_req(dev, cl)) {
-               cl->status = -ENODEV;
+       ret = mei_hbm_cl_connect_req(dev, cl);
+       if (ret) {
+               cl->status = ret;
                cb->buf_idx = 0;
                list_del(&cb->list);
-               return -ENODEV;
+               return ret;
        }
 
        list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
@@ -345,14 +349,14 @@ int mei_irq_read_handler(struct mei_device *dev,
 
        /* decide where to read the message too */
        if (!mei_hdr->host_addr) {
-               dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
+               dev_dbg(&dev->pdev->dev, "call mei_hbm_dispatch.\n");
                mei_hbm_dispatch(dev, mei_hdr);
-               dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
+               dev_dbg(&dev->pdev->dev, "end mei_hbm_dispatch.\n");
        } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
                   (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
                   (dev->iamthif_state == MEI_IAMTHIF_READING)) {
 
-               dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
+               dev_dbg(&dev->pdev->dev, "call mei_amthif_irq_read_msg.\n");
                dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
 
                ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list);
@@ -423,12 +427,12 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
                if (MEI_WRITING == cl->writing_state &&
                    cb->fop_type == MEI_FOP_WRITE &&
                    cl != &dev->iamthif_cl) {
-                       dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
+                       cl_dbg(dev, cl, "MEI WRITE COMPLETE\n");
                        cl->writing_state = MEI_WRITE_COMPLETE;
                        list_add_tail(&cb->list, &cmpl_list->list);
                }
                if (cl == &dev->iamthif_cl) {
-                       dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
+                       cl_dbg(dev, cl, "check iamthif flow control.\n");
                        if (dev->iamthif_flow_control_pending) {
                                ret = mei_amthif_irq_read(dev, &slots);
                                if (ret)
@@ -509,13 +513,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
                cl = cb->cl;
                if (cl == NULL)
                        continue;
-               if (mei_cl_flow_ctrl_creds(cl) <= 0) {
-                       dev_dbg(&dev->pdev->dev,
-                               "No flow control credentials for client %d, not sending.\n",
-                               cl->host_client_id);
-                       continue;
-               }
-
                if (cl == &dev->iamthif_cl)
                        ret = mei_amthif_irq_write_complete(cl, cb,
                                                &slots, cmpl_list);
index cabeddd66c1f406f73f7e5bed2a56b7e52cd7621..9661a812f55045d2cd522a7877ed8d4759f49b38 100644 (file)
@@ -60,48 +60,45 @@ static int mei_open(struct inode *inode, struct file *file)
 
        int err;
 
-       err = -ENODEV;
        if (!misc->parent)
-               goto out;
+               return -ENODEV;
 
        pdev = container_of(misc->parent, struct pci_dev, dev);
 
        dev = pci_get_drvdata(pdev);
        if (!dev)
-               goto out;
+               return -ENODEV;
 
        mutex_lock(&dev->device_lock);
-       err = -ENOMEM;
-       cl = mei_cl_allocate(dev);
-       if (!cl)
-               goto out_unlock;
+
+       cl = NULL;
 
        err = -ENODEV;
        if (dev->dev_state != MEI_DEV_ENABLED) {
                dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED  dev_state = %s\n",
                    mei_dev_state_str(dev->dev_state));
-               goto out_unlock;
-       }
-       err = -EMFILE;
-       if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
-               dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
-                       MEI_MAX_OPEN_HANDLE_COUNT);
-               goto out_unlock;
+               goto err_unlock;
        }
 
+       err = -ENOMEM;
+       cl = mei_cl_allocate(dev);
+       if (!cl)
+               goto err_unlock;
+
+       /* open_handle_count check is handled in the mei_cl_link */
        err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY);
        if (err)
-               goto out_unlock;
+               goto err_unlock;
 
        file->private_data = cl;
+
        mutex_unlock(&dev->device_lock);
 
        return nonseekable_open(inode, file);
 
-out_unlock:
+err_unlock:
        mutex_unlock(&dev->device_lock);
        kfree(cl);
-out:
        return err;
 }
 
@@ -144,10 +141,6 @@ static int mei_release(struct inode *inode, struct file *file)
            cl->host_client_id,
            cl->me_client_id);
 
-       if (dev->open_handle_count > 0) {
-               clear_bit(cl->host_client_id, dev->host_clients_map);
-               dev->open_handle_count--;
-       }
        mei_cl_unlink(cl);
 
 
@@ -165,10 +158,7 @@ static int mei_release(struct inode *inode, struct file *file)
 
        file->private_data = NULL;
 
-       if (cb) {
-               mei_io_cb_free(cb);
-               cb = NULL;
-       }
+       mei_io_cb_free(cb);
 
        kfree(cl);
 out:
@@ -203,12 +193,18 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
 
        dev = cl->dev;
 
+
        mutex_lock(&dev->device_lock);
        if (dev->dev_state != MEI_DEV_ENABLED) {
                rets = -ENODEV;
                goto out;
        }
 
+       if (length == 0) {
+               rets = 0;
+               goto out;
+       }
+
        if (cl == &dev->iamthif_cl) {
                rets = mei_amthif_read(dev, file, ubuf, length, offset);
                goto out;
@@ -347,8 +343,14 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                rets = -ENODEV;
                goto out;
        }
-       if (length > dev->me_clients[id].props.max_msg_length || length <= 0) {
-               rets = -EMSGSIZE;
+
+       if (length == 0) {
+               rets = 0;
+               goto out;
+       }
+
+       if (length > dev->me_clients[id].props.max_msg_length) {
+               rets = -EFBIG;
                goto out;
        }
 
@@ -401,8 +403,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                goto out;
 
        rets = copy_from_user(write_cb->request_buffer.data, ubuf, length);
-       if (rets)
+       if (rets) {
+               dev_err(&dev->pdev->dev, "failed to copy data from userland\n");
+               rets = -EFAULT;
                goto out;
+       }
 
        if (cl == &dev->iamthif_cl) {
                rets = mei_amthif_write(dev, write_cb);
@@ -489,11 +494,11 @@ static int mei_ioctl_connect_client(struct file *file,
                        rets = -ENODEV;
                        goto end;
                }
-               clear_bit(cl->host_client_id, dev->host_clients_map);
                mei_cl_unlink(cl);
 
                kfree(cl);
                cl = NULL;
+               dev->iamthif_open_count++;
                file->private_data = &dev->iamthif_cl;
 
                client = &data->out_client_properties;
@@ -564,7 +569,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
        dev_dbg(&dev->pdev->dev, "copy connect data from user\n");
        if (copy_from_user(connect_data, (char __user *)data,
                                sizeof(struct mei_connect_client_data))) {
-               dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n");
+               dev_err(&dev->pdev->dev, "failed to copy data from userland\n");
                rets = -EFAULT;
                goto out;
        }
index 456b322013e269fc61f911d6f5761768c2863d4f..406f68e05b4ed4ccdbc6d9f886386a5d110e307b 100644 (file)
@@ -414,6 +414,7 @@ struct mei_device {
        struct file *iamthif_file_object;
        struct mei_cl iamthif_cl;
        struct mei_cl_cb *iamthif_current_cb;
+       long iamthif_open_count;
        int iamthif_mtu;
        unsigned long iamthif_timer;
        u32 iamthif_stall_timer;
index d0c6907dfd926809620e5ca459781042e32ba093..994ca4aff1a37ecf1b6fc53a2b44e1f446b710dd 100644 (file)
@@ -485,8 +485,11 @@ int mei_nfc_host_init(struct mei_device *dev)
        if (ndev->cl_info)
                return 0;
 
-       cl_info = mei_cl_allocate(dev);
-       cl = mei_cl_allocate(dev);
+       ndev->cl_info = mei_cl_allocate(dev);
+       ndev->cl = mei_cl_allocate(dev);
+
+       cl = ndev->cl;
+       cl_info = ndev->cl_info;
 
        if (!cl || !cl_info) {
                ret = -ENOMEM;
@@ -527,10 +530,9 @@ int mei_nfc_host_init(struct mei_device *dev)
 
        cl->device_uuid = mei_nfc_guid;
 
+
        list_add_tail(&cl->device_link, &dev->device_list);
 
-       ndev->cl_info = cl_info;
-       ndev->cl = cl;
        ndev->req_id = 1;
 
        INIT_WORK(&ndev->init_work, mei_nfc_init);
index 1b3844e823792a0893ee9b200cbc7a681ee7025f..b96205aece0c781d267ef9c2ac198cc154145080 100644 (file)
@@ -77,6 +77,7 @@ static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = {
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_W)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)},
 
        /* required last entry */
@@ -189,7 +190,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        schedule_delayed_work(&dev->timer_work, HZ);
 
-       pr_debug("initialization successful.\n");
+       dev_dbg(&pdev->dev, "initialization successful.\n");
 
        return 0;
 
@@ -231,7 +232,7 @@ static void mei_me_remove(struct pci_dev *pdev)
        hw = to_me_hw(dev);
 
 
-       dev_err(&pdev->dev, "stop\n");
+       dev_dbg(&pdev->dev, "stop\n");
        mei_stop(dev);
 
        /* disable interrupts */
@@ -239,7 +240,6 @@ static void mei_me_remove(struct pci_dev *pdev)
 
        free_irq(pdev->irq, dev);
        pci_disable_msi(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        if (hw->mem_addr)
                pci_iounmap(pdev, hw->mem_addr);
@@ -262,7 +262,7 @@ static int mei_me_pci_suspend(struct device *device)
        if (!dev)
                return -ENODEV;
 
-       dev_err(&pdev->dev, "suspend\n");
+       dev_dbg(&pdev->dev, "suspend\n");
 
        mei_stop(dev);
 
index b8921432e89de3a350431b607f0daf1eeefb4757..9e354216c163d43d7ebe2f11241b2783d7f0a707 100644 (file)
@@ -60,7 +60,7 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
 int mei_wd_host_init(struct mei_device *dev)
 {
        struct mei_cl *cl = &dev->wd_cl;
-       int i;
+       int id;
        int ret;
 
        mei_cl_init(cl, dev);
@@ -70,19 +70,19 @@ int mei_wd_host_init(struct mei_device *dev)
 
 
        /* check for valid client id */
-       i = mei_me_cl_by_uuid(dev, &mei_wd_guid);
-       if (i < 0) {
+       id = mei_me_cl_by_uuid(dev, &mei_wd_guid);
+       if (id < 0) {
                dev_info(&dev->pdev->dev, "wd: failed to find the client\n");
-               return -ENOENT;
+               return id;
        }
 
-       cl->me_client_id = dev->me_clients[i].client_id;
+       cl->me_client_id = dev->me_clients[id].client_id;
 
        ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID);
 
        if (ret < 0) {
                dev_info(&dev->pdev->dev, "wd: failed link client\n");
-               return -ENOENT;
+               return ret;
        }
 
        cl->state = MEI_FILE_CONNECTING;
diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig
new file mode 100644 (file)
index 0000000..e42b331
--- /dev/null
@@ -0,0 +1,39 @@
+comment "Intel MIC Host Driver"
+
+config INTEL_MIC_HOST
+       tristate "Intel MIC Host Driver"
+       depends on 64BIT && PCI && X86
+       select VHOST_RING
+       default N
+       help
+         This enables Host Driver support for the Intel Many Integrated
+         Core (MIC) family of PCIe form factor coprocessor devices that
+         run a 64 bit Linux OS. The driver manages card OS state and
+         enables communication between host and card. Intel MIC X100
+         devices are currently supported.
+
+         If you are building a host kernel with an Intel MIC device then
+         say M (recommended) or Y, else say N. If unsure say N.
+
+         More information about the Intel MIC family as well as the Linux
+         OS and tools for MIC to use with this driver are available from
+         <http://software.intel.com/en-us/mic-developer>.
+
+comment "Intel MIC Card Driver"
+
+config INTEL_MIC_CARD
+       tristate "Intel MIC Card Driver"
+       depends on 64BIT && X86
+       select VIRTIO
+       default N
+       help
+         This enables card driver support for the Intel Many Integrated
+         Core (MIC) device family. The card driver communicates shutdown/
+         crash events to the host and allows registration/configuration of
+         virtio devices. Intel MIC X100 devices are currently supported.
+
+         If you are building a card kernel for an Intel MIC device then
+         say M (recommended) or Y, else say N. If unsure say N.
+
+         For more information see
+         <http://software.intel.com/en-us/mic-developer>.
diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile
new file mode 100644 (file)
index 0000000..05b34d6
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile - Intel MIC Linux driver.
+# Copyright(c) 2013, Intel Corporation.
+#
+obj-$(CONFIG_INTEL_MIC_HOST) += host/
+obj-$(CONFIG_INTEL_MIC_CARD) += card/
diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile
new file mode 100644 (file)
index 0000000..69d58be
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Makefile - Intel MIC Linux driver.
+# Copyright(c) 2013, Intel Corporation.
+#
+ccflags-y += -DINTEL_MIC_CARD
+
+obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o
+mic_card-y += mic_x100.o
+mic_card-y += mic_device.o
+mic_card-y += mic_debugfs.o
+mic_card-y += mic_virtio.o
diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c
new file mode 100644 (file)
index 0000000..421b3d7
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+
+/* Debugfs parent dir */
+static struct dentry *mic_dbg;
+
+/**
+ * mic_intr_test - Send interrupts to host.
+ */
+static int mic_intr_test(struct seq_file *s, void *unused)
+{
+       struct mic_driver *mdrv = s->private;
+       struct mic_device *mdev = &mdrv->mdev;
+
+       mic_send_intr(mdev, 0);
+       msleep(1000);
+       mic_send_intr(mdev, 1);
+       msleep(1000);
+       mic_send_intr(mdev, 2);
+       msleep(1000);
+       mic_send_intr(mdev, 3);
+       msleep(1000);
+
+       return 0;
+}
+
+static int mic_intr_test_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_intr_test, inode->i_private);
+}
+
+static int mic_intr_test_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations intr_test_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_intr_test_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_intr_test_release
+};
+
+/**
+ * mic_create_card_debug_dir - Initialize MIC debugfs entries.
+ */
+void __init mic_create_card_debug_dir(struct mic_driver *mdrv)
+{
+       struct dentry *d;
+
+       if (!mic_dbg)
+               return;
+
+       mdrv->dbg_dir = debugfs_create_dir(mdrv->name, mic_dbg);
+       if (!mdrv->dbg_dir) {
+               dev_err(mdrv->dev, "Cant create dbg_dir %s\n", mdrv->name);
+               return;
+       }
+
+       d = debugfs_create_file("intr_test", 0444, mdrv->dbg_dir,
+               mdrv, &intr_test_ops);
+
+       if (!d) {
+               dev_err(mdrv->dev,
+                       "Cant create dbg intr_test %s\n", mdrv->name);
+               return;
+       }
+}
+
+/**
+ * mic_delete_card_debug_dir - Uninitialize MIC debugfs entries.
+ */
+void mic_delete_card_debug_dir(struct mic_driver *mdrv)
+{
+       if (!mdrv->dbg_dir)
+               return;
+
+       debugfs_remove_recursive(mdrv->dbg_dir);
+}
+
+/**
+ * mic_init_card_debugfs - Initialize global debugfs entry.
+ */
+void __init mic_init_card_debugfs(void)
+{
+       mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
+       if (!mic_dbg)
+               pr_err("can't create debugfs dir\n");
+}
+
+/**
+ * mic_exit_card_debugfs - Uninitialize global debugfs entry
+ */
+void mic_exit_card_debugfs(void)
+{
+       debugfs_remove(mic_dbg);
+}
diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c
new file mode 100644 (file)
index 0000000..d0980ff
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_virtio.h"
+
+static struct mic_driver *g_drv;
+static struct mic_irq *shutdown_cookie;
+
+static void mic_notify_host(u8 state)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+
+       iowrite8(state, &bootparam->shutdown_status);
+       dev_dbg(mdrv->dev, "%s %d system_state %d\n",
+               __func__, __LINE__, state);
+       mic_send_intr(&mdrv->mdev, ioread8(&bootparam->c2h_shutdown_db));
+}
+
+static int mic_panic_event(struct notifier_block *this, unsigned long event,
+               void *ptr)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+
+       iowrite8(-1, &bootparam->h2c_config_db);
+       iowrite8(-1, &bootparam->h2c_shutdown_db);
+       mic_notify_host(MIC_CRASHED);
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block mic_panic = {
+       .notifier_call  = mic_panic_event,
+};
+
+static irqreturn_t mic_shutdown_isr(int irq, void *data)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+
+       mic_ack_interrupt(&g_drv->mdev);
+       if (ioread8(&bootparam->shutdown_card))
+               orderly_poweroff(true);
+       return IRQ_HANDLED;
+}
+
+static int mic_shutdown_init(void)
+{
+       int rc = 0;
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+       int shutdown_db;
+
+       shutdown_db = mic_next_card_db();
+       shutdown_cookie = mic_request_card_irq(mic_shutdown_isr,
+                       "Shutdown", mdrv, shutdown_db);
+       if (IS_ERR(shutdown_cookie))
+               rc = PTR_ERR(shutdown_cookie);
+       else
+               iowrite8(shutdown_db, &bootparam->h2c_shutdown_db);
+       return rc;
+}
+
+static void mic_shutdown_uninit(void)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+
+       iowrite8(-1, &bootparam->h2c_shutdown_db);
+       mic_free_card_irq(shutdown_cookie, mdrv);
+}
+
+static int __init mic_dp_init(void)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_device *mdev = &mdrv->mdev;
+       struct mic_bootparam __iomem *bootparam;
+       u64 lo, hi, dp_dma_addr;
+       u32 magic;
+
+       lo = mic_read_spad(&mdrv->mdev, MIC_DPLO_SPAD);
+       hi = mic_read_spad(&mdrv->mdev, MIC_DPHI_SPAD);
+
+       dp_dma_addr = lo | (hi << 32);
+       mdrv->dp = mic_card_map(mdev, dp_dma_addr, MIC_DP_SIZE);
+       if (!mdrv->dp) {
+               dev_err(mdrv->dev, "Cannot remap Aperture BAR\n");
+               return -ENOMEM;
+       }
+       bootparam = mdrv->dp;
+       magic = ioread32(&bootparam->magic);
+       if (MIC_MAGIC != magic) {
+               dev_err(mdrv->dev, "bootparam magic mismatch 0x%x\n", magic);
+               return -EIO;
+       }
+       return 0;
+}
+
+/* Uninitialize the device page */
+static void mic_dp_uninit(void)
+{
+       mic_card_unmap(&g_drv->mdev, g_drv->dp);
+}
+
+/**
+ * mic_request_card_irq - request an irq.
+ *
+ * @func: The callback function that handles the interrupt.
+ * @name: The ASCII name of the callee requesting the irq.
+ * @data: private data that is returned back when calling the
+ * function handler.
+ * @index: The doorbell index of the requester.
+ *
+ * returns: The cookie that is transparent to the caller. Passed
+ * back when calling mic_free_irq. An appropriate error code
+ * is returned on failure. Caller needs to use IS_ERR(return_val)
+ * to check for failure and PTR_ERR(return_val) to obtained the
+ * error code.
+ *
+ */
+struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
+       const char *name, void *data, int index)
+{
+       int rc = 0;
+       unsigned long cookie;
+       struct mic_driver *mdrv = g_drv;
+
+       rc  = request_irq(mic_db_to_irq(mdrv, index), func,
+               0, name, data);
+       if (rc) {
+               dev_err(mdrv->dev, "request_irq failed rc = %d\n", rc);
+               goto err;
+       }
+       mdrv->irq_info.irq_usage_count[index]++;
+       cookie = index;
+       return (struct mic_irq *)cookie;
+err:
+       return ERR_PTR(rc);
+}
+
+/**
+ * mic_free_card_irq - free irq.
+ *
+ * @cookie: cookie obtained during a successful call to mic_request_irq
+ * @data: private data specified by the calling function during the
+ * mic_request_irq
+ *
+ * returns: none.
+ */
+void mic_free_card_irq(struct mic_irq *cookie, void *data)
+{
+       int index;
+       struct mic_driver *mdrv = g_drv;
+
+       index = (unsigned long)cookie & 0xFFFFU;
+       free_irq(mic_db_to_irq(mdrv, index), data);
+       mdrv->irq_info.irq_usage_count[index]--;
+}
+
+/**
+ * mic_next_card_db - Get the doorbell with minimum usage count.
+ *
+ * Returns the irq index.
+ */
+int mic_next_card_db(void)
+{
+       int i;
+       int index = 0;
+       struct mic_driver *mdrv = g_drv;
+
+       for (i = 0; i < mdrv->intr_info.num_intr; i++) {
+               if (mdrv->irq_info.irq_usage_count[i] <
+                       mdrv->irq_info.irq_usage_count[index])
+                       index = i;
+       }
+
+       return index;
+}
+
+/**
+ * mic_init_irq - Initialize irq information.
+ *
+ * Returns 0 in success. Appropriate error code on failure.
+ */
+static int mic_init_irq(void)
+{
+       struct mic_driver *mdrv = g_drv;
+
+       mdrv->irq_info.irq_usage_count = kzalloc((sizeof(u32) *
+                       mdrv->intr_info.num_intr),
+                       GFP_KERNEL);
+       if (!mdrv->irq_info.irq_usage_count)
+               return -ENOMEM;
+       return 0;
+}
+
+/**
+ * mic_uninit_irq - Uninitialize irq information.
+ *
+ * None.
+ */
+static void mic_uninit_irq(void)
+{
+       struct mic_driver *mdrv = g_drv;
+
+       kfree(mdrv->irq_info.irq_usage_count);
+}
+
+/*
+ * mic_driver_init - MIC driver initialization tasks.
+ *
+ * Returns 0 in success. Appropriate error code on failure.
+ */
+int __init mic_driver_init(struct mic_driver *mdrv)
+{
+       int rc;
+
+       g_drv = mdrv;
+       /*
+        * Unloading the card module is not supported. The MIC card module
+        * handles fundamental operations like host/card initiated shutdowns
+        * and informing the host about card crashes and cannot be unloaded.
+        */
+       if (!try_module_get(mdrv->dev->driver->owner)) {
+               rc = -ENODEV;
+               goto done;
+       }
+       rc = mic_dp_init();
+       if (rc)
+               goto put;
+       rc = mic_init_irq();
+       if (rc)
+               goto dp_uninit;
+       rc = mic_shutdown_init();
+       if (rc)
+               goto irq_uninit;
+       rc = mic_devices_init(mdrv);
+       if (rc)
+               goto shutdown_uninit;
+       mic_create_card_debug_dir(mdrv);
+       atomic_notifier_chain_register(&panic_notifier_list, &mic_panic);
+done:
+       return rc;
+shutdown_uninit:
+       mic_shutdown_uninit();
+irq_uninit:
+       mic_uninit_irq();
+dp_uninit:
+       mic_dp_uninit();
+put:
+       module_put(mdrv->dev->driver->owner);
+       return rc;
+}
+
+/*
+ * mic_driver_uninit - MIC driver uninitialization tasks.
+ *
+ * Returns None
+ */
+void mic_driver_uninit(struct mic_driver *mdrv)
+{
+       mic_delete_card_debug_dir(mdrv);
+       mic_devices_uninit(mdrv);
+       /*
+        * Inform the host about the shutdown status i.e. poweroff/restart etc.
+        * The module cannot be unloaded so the only code path to call
+        * mic_devices_uninit(..) is the shutdown callback.
+        */
+       mic_notify_host(system_state);
+       mic_shutdown_uninit();
+       mic_uninit_irq();
+       mic_dp_uninit();
+       module_put(mdrv->dev->driver->owner);
+}
diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h
new file mode 100644 (file)
index 0000000..347b9b3
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#ifndef _MIC_CARD_DEVICE_H_
+#define _MIC_CARD_DEVICE_H_
+
+#include <linux/workqueue.h>
+#include <linux/io.h>
+
+/**
+ * struct mic_intr_info - Contains h/w specific interrupt sources info
+ *
+ * @num_intr: The number of irqs available
+ */
+struct mic_intr_info {
+       u32 num_intr;
+};
+
+/**
+ * struct mic_irq_info - OS specific irq information
+ *
+ * @irq_usage_count: usage count array tracking the number of sources
+ * assigned for each irq.
+ */
+struct mic_irq_info {
+       int *irq_usage_count;
+};
+
+/**
+ * struct mic_device -  MIC device information.
+ *
+ * @mmio: MMIO bar information.
+ */
+struct mic_device {
+       struct mic_mw mmio;
+};
+
+/**
+ * struct mic_driver - MIC card driver information.
+ *
+ * @name: Name for MIC driver.
+ * @dbg_dir: debugfs directory of this MIC device.
+ * @dev: The device backing this MIC.
+ * @dp: The pointer to the virtio device page.
+ * @mdev: MIC device information for the host.
+ * @hotplug_work: Hot plug work for adding/removing virtio devices.
+ * @irq_info: The OS specific irq information
+ * @intr_info: H/W specific interrupt information.
+ */
+struct mic_driver {
+       char name[20];
+       struct dentry *dbg_dir;
+       struct device *dev;
+       void __iomem *dp;
+       struct mic_device mdev;
+       struct work_struct hotplug_work;
+       struct mic_irq_info irq_info;
+       struct mic_intr_info intr_info;
+};
+
+/**
+ * struct mic_irq - opaque pointer used as cookie
+ */
+struct mic_irq;
+
+/**
+ * mic_mmio_read - read from an MMIO register.
+ * @mw: MMIO register base virtual address.
+ * @offset: register offset.
+ *
+ * RETURNS: register value.
+ */
+static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset)
+{
+       return ioread32(mw->va + offset);
+}
+
+/**
+ * mic_mmio_write - write to an MMIO register.
+ * @mw: MMIO register base virtual address.
+ * @val: the data value to put into the register
+ * @offset: register offset.
+ *
+ * RETURNS: none.
+ */
+static inline void
+mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset)
+{
+       iowrite32(val, mw->va + offset);
+}
+
+int mic_driver_init(struct mic_driver *mdrv);
+void mic_driver_uninit(struct mic_driver *mdrv);
+int mic_next_card_db(void);
+struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
+       const char *name, void *data, int intr_src);
+void mic_free_card_irq(struct mic_irq *cookie, void *data);
+u32 mic_read_spad(struct mic_device *mdev, unsigned int idx);
+void mic_send_intr(struct mic_device *mdev, int doorbell);
+int mic_db_to_irq(struct mic_driver *mdrv, int db);
+u32 mic_ack_interrupt(struct mic_device *mdev);
+void mic_hw_intr_init(struct mic_driver *mdrv);
+void __iomem *
+mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size);
+void mic_card_unmap(struct mic_device *mdev, void __iomem *addr);
+void __init mic_create_card_debug_dir(struct mic_driver *mdrv);
+void mic_delete_card_debug_dir(struct mic_driver *mdrv);
+void __init mic_init_card_debugfs(void);
+void mic_exit_card_debugfs(void);
+#endif
diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c
new file mode 100644 (file)
index 0000000..914cc9b
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Adapted from:
+ *
+ * virtio for kvm on s390
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): Christian Borntraeger <borntraeger@de.ibm.com>
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/virtio_config.h>
+
+#include "../common/mic_dev.h"
+#include "mic_virtio.h"
+
+#define VIRTIO_SUBCODE_64 0x0D00
+
+#define MIC_MAX_VRINGS                4
+struct mic_vdev {
+       struct virtio_device vdev;
+       struct mic_device_desc __iomem *desc;
+       struct mic_device_ctrl __iomem *dc;
+       struct mic_device *mdev;
+       void __iomem *vr[MIC_MAX_VRINGS];
+       int used_size[MIC_MAX_VRINGS];
+       struct completion reset_done;
+       struct mic_irq *virtio_cookie;
+       int c2h_vdev_db;
+};
+
+static struct mic_irq *virtio_config_cookie;
+#define to_micvdev(vd) container_of(vd, struct mic_vdev, vdev)
+
+/* Helper API to obtain the parent of the virtio device */
+static inline struct device *mic_dev(struct mic_vdev *mvdev)
+{
+       return mvdev->vdev.dev.parent;
+}
+
+/* This gets the device's feature bits. */
+static u32 mic_get_features(struct virtio_device *vdev)
+{
+       unsigned int i, bits;
+       u32 features = 0;
+       struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc;
+       u8 __iomem *in_features = mic_vq_features(desc);
+       int feature_len = ioread8(&desc->feature_len);
+
+       bits = min_t(unsigned, feature_len,
+               sizeof(vdev->features)) * 8;
+       for (i = 0; i < bits; i++)
+               if (ioread8(&in_features[i / 8]) & (BIT(i % 8)))
+                       features |= BIT(i);
+
+       return features;
+}
+
+static void mic_finalize_features(struct virtio_device *vdev)
+{
+       unsigned int i, bits;
+       struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc;
+       u8 feature_len = ioread8(&desc->feature_len);
+       /* Second half of bitmap is features we accept. */
+       u8 __iomem *out_features =
+               mic_vq_features(desc) + feature_len;
+
+       /* Give virtio_ring a chance to accept features. */
+       vring_transport_features(vdev);
+
+       memset_io(out_features, 0, feature_len);
+       bits = min_t(unsigned, feature_len,
+               sizeof(vdev->features)) * 8;
+       for (i = 0; i < bits; i++) {
+               if (test_bit(i, vdev->features))
+                       iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)),
+                                &out_features[i / 8]);
+       }
+}
+
+/*
+ * Reading and writing elements in config space
+ */
+static void mic_get(struct virtio_device *vdev, unsigned int offset,
+                  void *buf, unsigned len)
+{
+       struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc;
+
+       if (offset + len > ioread8(&desc->config_len))
+               return;
+       memcpy_fromio(buf, mic_vq_configspace(desc) + offset, len);
+}
+
+static void mic_set(struct virtio_device *vdev, unsigned int offset,
+                  const void *buf, unsigned len)
+{
+       struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc;
+
+       if (offset + len > ioread8(&desc->config_len))
+               return;
+       memcpy_toio(mic_vq_configspace(desc) + offset, buf, len);
+}
+
+/*
+ * The operations to get and set the status word just access the status
+ * field of the device descriptor. set_status also interrupts the host
+ * to tell about status changes.
+ */
+static u8 mic_get_status(struct virtio_device *vdev)
+{
+       return ioread8(&to_micvdev(vdev)->desc->status);
+}
+
+static void mic_set_status(struct virtio_device *vdev, u8 status)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       if (!status)
+               return;
+       iowrite8(status, &mvdev->desc->status);
+       mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db);
+}
+
+/* Inform host on a virtio device reset and wait for ack from host */
+static void mic_reset_inform_host(struct virtio_device *vdev)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       struct mic_device_ctrl __iomem *dc = mvdev->dc;
+       int retry = 100, i;
+
+       iowrite8(0, &dc->host_ack);
+       iowrite8(1, &dc->vdev_reset);
+       mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db);
+
+       /* Wait till host completes all card accesses and acks the reset */
+       for (i = retry; i--;) {
+               if (ioread8(&dc->host_ack))
+                       break;
+               msleep(100);
+       };
+
+       dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry);
+
+       /* Reset status to 0 in case we timed out */
+       iowrite8(0, &mvdev->desc->status);
+}
+
+static void mic_reset(struct virtio_device *vdev)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+
+       dev_dbg(mic_dev(mvdev), "%s: virtio id %d\n",
+               __func__, vdev->id.device);
+
+       mic_reset_inform_host(vdev);
+       complete_all(&mvdev->reset_done);
+}
+
+/*
+ * The virtio_ring code calls this API when it wants to notify the Host.
+ */
+static void mic_notify(struct virtqueue *vq)
+{
+       struct mic_vdev *mvdev = vq->priv;
+
+       mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db);
+}
+
+static void mic_del_vq(struct virtqueue *vq, int n)
+{
+       struct mic_vdev *mvdev = to_micvdev(vq->vdev);
+       struct vring *vr = (struct vring *)(vq + 1);
+
+       free_pages((unsigned long) vr->used, get_order(mvdev->used_size[n]));
+       vring_del_virtqueue(vq);
+       mic_card_unmap(mvdev->mdev, mvdev->vr[n]);
+       mvdev->vr[n] = NULL;
+}
+
+static void mic_del_vqs(struct virtio_device *vdev)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       struct virtqueue *vq, *n;
+       int idx = 0;
+
+       dev_dbg(mic_dev(mvdev), "%s\n", __func__);
+
+       list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+               mic_del_vq(vq, idx++);
+}
+
+/*
+ * This routine will assign vring's allocated in host/io memory. Code in
+ * virtio_ring.c however continues to access this io memory as if it were local
+ * memory without io accessors.
+ */
+static struct virtqueue *mic_find_vq(struct virtio_device *vdev,
+                                    unsigned index,
+                                    void (*callback)(struct virtqueue *vq),
+                                    const char *name)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       struct mic_vqconfig __iomem *vqconfig;
+       struct mic_vqconfig config;
+       struct virtqueue *vq;
+       void __iomem *va;
+       struct _mic_vring_info __iomem *info;
+       void *used;
+       int vr_size, _vr_size, err, magic;
+       struct vring *vr;
+       u8 type = ioread8(&mvdev->desc->type);
+
+       if (index >= ioread8(&mvdev->desc->num_vq))
+               return ERR_PTR(-ENOENT);
+
+       if (!name)
+               return ERR_PTR(-ENOENT);
+
+       /* First assign the vring's allocated in host memory */
+       vqconfig = mic_vq_config(mvdev->desc) + index;
+       memcpy_fromio(&config, vqconfig, sizeof(config));
+       _vr_size = vring_size(config.num, MIC_VIRTIO_RING_ALIGN);
+       vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info));
+       va = mic_card_map(mvdev->mdev, config.address, vr_size);
+       if (!va)
+               return ERR_PTR(-ENOMEM);
+       mvdev->vr[index] = va;
+       memset_io(va, 0x0, _vr_size);
+       vq = vring_new_virtqueue(index,
+                               config.num, MIC_VIRTIO_RING_ALIGN, vdev,
+                               false,
+                               va, mic_notify, callback, name);
+       if (!vq) {
+               err = -ENOMEM;
+               goto unmap;
+       }
+       info = va + _vr_size;
+       magic = ioread32(&info->magic);
+
+       if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) {
+               err = -EIO;
+               goto unmap;
+       }
+
+       /* Allocate and reassign used ring now */
+       mvdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 +
+                       sizeof(struct vring_used_elem) * config.num);
+       used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+                                       get_order(mvdev->used_size[index]));
+       if (!used) {
+               err = -ENOMEM;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+               goto del_vq;
+       }
+       iowrite64(virt_to_phys(used), &vqconfig->used_address);
+
+       /*
+        * To reassign the used ring here we are directly accessing
+        * struct vring_virtqueue which is a private data structure
+        * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in
+        * vring_new_virtqueue() would ensure that
+        *  (&vq->vring == (struct vring *) (&vq->vq + 1));
+        */
+       vr = (struct vring *)(vq + 1);
+       vr->used = used;
+
+       vq->priv = mvdev;
+       return vq;
+del_vq:
+       vring_del_virtqueue(vq);
+unmap:
+       mic_card_unmap(mvdev->mdev, mvdev->vr[index]);
+       return ERR_PTR(err);
+}
+
+static int mic_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+                       struct virtqueue *vqs[],
+                       vq_callback_t *callbacks[],
+                       const char *names[])
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       struct mic_device_ctrl __iomem *dc = mvdev->dc;
+       int i, err, retry = 100;
+
+       /* We must have this many virtqueues. */
+       if (nvqs > ioread8(&mvdev->desc->num_vq))
+               return -ENOENT;
+
+       for (i = 0; i < nvqs; ++i) {
+               dev_dbg(mic_dev(mvdev), "%s: %d: %s\n",
+                       __func__, i, names[i]);
+               vqs[i] = mic_find_vq(vdev, i, callbacks[i], names[i]);
+               if (IS_ERR(vqs[i])) {
+                       err = PTR_ERR(vqs[i]);
+                       goto error;
+               }
+       }
+
+       iowrite8(1, &dc->used_address_updated);
+       /*
+        * Send an interrupt to the host to inform it that used
+        * rings have been re-assigned.
+        */
+       mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db);
+       for (i = retry; i--;) {
+               if (!ioread8(&dc->used_address_updated))
+                       break;
+               msleep(100);
+       };
+
+       dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry);
+       if (!retry) {
+               err = -ENODEV;
+               goto error;
+       }
+
+       return 0;
+error:
+       mic_del_vqs(vdev);
+       return err;
+}
+
+/*
+ * The config ops structure as defined by virtio config
+ */
+static struct virtio_config_ops mic_vq_config_ops = {
+       .get_features = mic_get_features,
+       .finalize_features = mic_finalize_features,
+       .get = mic_get,
+       .set = mic_set,
+       .get_status = mic_get_status,
+       .set_status = mic_set_status,
+       .reset = mic_reset,
+       .find_vqs = mic_find_vqs,
+       .del_vqs = mic_del_vqs,
+};
+
+static irqreturn_t
+mic_virtio_intr_handler(int irq, void *data)
+{
+       struct mic_vdev *mvdev = data;
+       struct virtqueue *vq;
+
+       mic_ack_interrupt(mvdev->mdev);
+       list_for_each_entry(vq, &mvdev->vdev.vqs, list)
+               vring_interrupt(0, vq);
+
+       return IRQ_HANDLED;
+}
+
+static void mic_virtio_release_dev(struct device *_d)
+{
+       /*
+        * No need for a release method similar to virtio PCI.
+        * Provide an empty one to avoid getting a warning from core.
+        */
+}
+
+/*
+ * adds a new device and register it with virtio
+ * appropriate drivers are loaded by the device model
+ */
+static int mic_add_device(struct mic_device_desc __iomem *d,
+       unsigned int offset, struct mic_driver *mdrv)
+{
+       struct mic_vdev *mvdev;
+       int ret;
+       int virtio_db;
+       u8 type = ioread8(&d->type);
+
+       mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL);
+       if (!mvdev) {
+               dev_err(mdrv->dev, "Cannot allocate mic dev %u type %u\n",
+                       offset, type);
+               return -ENOMEM;
+       }
+
+       mvdev->mdev = &mdrv->mdev;
+       mvdev->vdev.dev.parent = mdrv->dev;
+       mvdev->vdev.dev.release = mic_virtio_release_dev;
+       mvdev->vdev.id.device = type;
+       mvdev->vdev.config = &mic_vq_config_ops;
+       mvdev->desc = d;
+       mvdev->dc = (void __iomem *)d + mic_aligned_desc_size(d);
+       init_completion(&mvdev->reset_done);
+
+       virtio_db = mic_next_card_db();
+       mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler,
+                       "virtio intr", mvdev, virtio_db);
+       if (IS_ERR(mvdev->virtio_cookie)) {
+               ret = PTR_ERR(mvdev->virtio_cookie);
+               goto kfree;
+       }
+       iowrite8((u8)virtio_db, &mvdev->dc->h2c_vdev_db);
+       mvdev->c2h_vdev_db = ioread8(&mvdev->dc->c2h_vdev_db);
+
+       ret = register_virtio_device(&mvdev->vdev);
+       if (ret) {
+               dev_err(mic_dev(mvdev),
+                       "Failed to register mic device %u type %u\n",
+                       offset, type);
+               goto free_irq;
+       }
+       iowrite64((u64)mvdev, &mvdev->dc->vdev);
+       dev_dbg(mic_dev(mvdev), "%s: registered mic device %u type %u mvdev %p\n",
+               __func__, offset, type, mvdev);
+
+       return 0;
+
+free_irq:
+       mic_free_card_irq(mvdev->virtio_cookie, mvdev);
+kfree:
+       kfree(mvdev);
+       return ret;
+}
+
+/*
+ * match for a mic device with a specific desc pointer
+ */
+static int mic_match_desc(struct device *dev, void *data)
+{
+       struct virtio_device *vdev = dev_to_virtio(dev);
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+
+       return mvdev->desc == (void __iomem *)data;
+}
+
+static void mic_handle_config_change(struct mic_device_desc __iomem *d,
+       unsigned int offset, struct mic_driver *mdrv)
+{
+       struct mic_device_ctrl __iomem *dc
+               = (void __iomem *)d + mic_aligned_desc_size(d);
+       struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev);
+       struct virtio_driver *drv;
+
+       if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED)
+               return;
+
+       dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__);
+       drv = container_of(mvdev->vdev.dev.driver,
+                               struct virtio_driver, driver);
+       if (drv->config_changed)
+               drv->config_changed(&mvdev->vdev);
+       iowrite8(1, &dc->guest_ack);
+}
+
+/*
+ * removes a virtio device if a hot remove event has been
+ * requested by the host.
+ */
+static int mic_remove_device(struct mic_device_desc __iomem *d,
+       unsigned int offset, struct mic_driver *mdrv)
+{
+       struct mic_device_ctrl __iomem *dc
+               = (void __iomem *)d + mic_aligned_desc_size(d);
+       struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev);
+       u8 status;
+       int ret = -1;
+
+       if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) {
+               dev_dbg(mdrv->dev,
+                       "%s %d config_change %d type %d mvdev %p\n",
+                       __func__, __LINE__,
+                       ioread8(&dc->config_change), ioread8(&d->type), mvdev);
+
+               status = ioread8(&d->status);
+               INIT_COMPLETION(mvdev->reset_done);
+               unregister_virtio_device(&mvdev->vdev);
+               mic_free_card_irq(mvdev->virtio_cookie, mvdev);
+               if (status & VIRTIO_CONFIG_S_DRIVER_OK)
+                       wait_for_completion(&mvdev->reset_done);
+               kfree(mvdev);
+               iowrite8(1, &dc->guest_ack);
+               dev_dbg(mdrv->dev, "%s %d guest_ack %d\n",
+                       __func__, __LINE__, ioread8(&dc->guest_ack));
+               ret = 0;
+       }
+
+       return ret;
+}
+
+#define REMOVE_DEVICES true
+
+static void mic_scan_devices(struct mic_driver *mdrv, bool remove)
+{
+       s8 type;
+       unsigned int i;
+       struct mic_device_desc __iomem *d;
+       struct mic_device_ctrl __iomem *dc;
+       struct device *dev;
+       int ret;
+
+       for (i = mic_aligned_size(struct mic_bootparam);
+               i < MIC_DP_SIZE; i += mic_total_desc_size(d)) {
+               d = mdrv->dp + i;
+               dc = (void __iomem *)d + mic_aligned_desc_size(d);
+               /*
+                * This read barrier is paired with the corresponding write
+                * barrier on the host which is inserted before adding or
+                * removing a virtio device descriptor, by updating the type.
+                */
+               rmb();
+               type = ioread8(&d->type);
+
+               /* end of list */
+               if (type == 0)
+                       break;
+
+               if (type == -1)
+                       continue;
+
+               /* device already exists */
+               dev = device_find_child(mdrv->dev, d, mic_match_desc);
+               if (dev) {
+                       if (remove)
+                               iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE,
+                                        &dc->config_change);
+                       put_device(dev);
+                       mic_handle_config_change(d, i, mdrv);
+                       ret = mic_remove_device(d, i, mdrv);
+                       if (!ret && !remove)
+                               iowrite8(-1, &d->type);
+                       if (remove) {
+                               iowrite8(0, &dc->config_change);
+                               iowrite8(0, &dc->guest_ack);
+                       }
+                       continue;
+               }
+
+               /* new device */
+               dev_dbg(mdrv->dev, "%s %d Adding new virtio device %p\n",
+                       __func__, __LINE__, d);
+               if (!remove)
+                       mic_add_device(d, i, mdrv);
+       }
+}
+
+/*
+ * mic_hotplug_device tries to find changes in the device page.
+ */
+static void mic_hotplug_devices(struct work_struct *work)
+{
+       struct mic_driver *mdrv = container_of(work,
+               struct mic_driver, hotplug_work);
+
+       mic_scan_devices(mdrv, !REMOVE_DEVICES);
+}
+
+/*
+ * Interrupt handler for hot plug/config changes etc.
+ */
+static irqreturn_t
+mic_extint_handler(int irq, void *data)
+{
+       struct mic_driver *mdrv = (struct mic_driver *)data;
+
+       dev_dbg(mdrv->dev, "%s %d hotplug work\n",
+               __func__, __LINE__);
+       mic_ack_interrupt(&mdrv->mdev);
+       schedule_work(&mdrv->hotplug_work);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Init function for virtio
+ */
+int mic_devices_init(struct mic_driver *mdrv)
+{
+       int rc;
+       struct mic_bootparam __iomem *bootparam;
+       int config_db;
+
+       INIT_WORK(&mdrv->hotplug_work, mic_hotplug_devices);
+       mic_scan_devices(mdrv, !REMOVE_DEVICES);
+
+       config_db = mic_next_card_db();
+       virtio_config_cookie = mic_request_card_irq(mic_extint_handler,
+                       "virtio_config_intr", mdrv, config_db);
+       if (IS_ERR(virtio_config_cookie)) {
+               rc = PTR_ERR(virtio_config_cookie);
+               goto exit;
+       }
+
+       bootparam = mdrv->dp;
+       iowrite8(config_db, &bootparam->h2c_config_db);
+       return 0;
+exit:
+       return rc;
+}
+
+/*
+ * Uninit function for virtio
+ */
+void mic_devices_uninit(struct mic_driver *mdrv)
+{
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+       iowrite8(-1, &bootparam->h2c_config_db);
+       mic_free_card_irq(virtio_config_cookie, mdrv);
+       flush_work(&mdrv->hotplug_work);
+       mic_scan_devices(mdrv, REMOVE_DEVICES);
+}
diff --git a/drivers/misc/mic/card/mic_virtio.h b/drivers/misc/mic/card/mic_virtio.h
new file mode 100644 (file)
index 0000000..2c5c22c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#ifndef __MIC_CARD_VIRTIO_H
+#define __MIC_CARD_VIRTIO_H
+
+#include <linux/mic_common.h>
+#include "mic_device.h"
+
+/*
+ * 64 bit I/O access
+ */
+#ifndef ioread64
+#define ioread64 readq
+#endif
+#ifndef iowrite64
+#define iowrite64 writeq
+#endif
+
+static inline unsigned mic_desc_size(struct mic_device_desc __iomem *desc)
+{
+       return mic_aligned_size(*desc)
+               + ioread8(&desc->num_vq) * mic_aligned_size(struct mic_vqconfig)
+               + ioread8(&desc->feature_len) * 2
+               + ioread8(&desc->config_len);
+}
+
+static inline struct mic_vqconfig __iomem *
+mic_vq_config(struct mic_device_desc __iomem *desc)
+{
+       return (struct mic_vqconfig __iomem *)(desc + 1);
+}
+
+static inline __u8 __iomem *
+mic_vq_features(struct mic_device_desc __iomem *desc)
+{
+       return (__u8 __iomem *)(mic_vq_config(desc) + ioread8(&desc->num_vq));
+}
+
+static inline __u8 __iomem *
+mic_vq_configspace(struct mic_device_desc __iomem *desc)
+{
+       return mic_vq_features(desc) + ioread8(&desc->feature_len) * 2;
+}
+static inline unsigned mic_total_desc_size(struct mic_device_desc __iomem *desc)
+{
+       return mic_aligned_desc_size(desc) +
+               mic_aligned_size(struct mic_device_ctrl);
+}
+
+int mic_devices_init(struct mic_driver *mdrv);
+void mic_devices_uninit(struct mic_driver *mdrv);
+
+#endif
diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c
new file mode 100644 (file)
index 0000000..2868945
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_x100.h"
+
+static const char mic_driver_name[] = "mic";
+
+static struct mic_driver g_drv;
+
+/**
+ * mic_read_spad - read from the scratchpad register
+ * @mdev: pointer to mic_device instance
+ * @idx: index to scratchpad register, 0 based
+ *
+ * This function allows reading of the 32bit scratchpad register.
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+u32 mic_read_spad(struct mic_device *mdev, unsigned int idx)
+{
+       return mic_mmio_read(&mdev->mmio,
+               MIC_X100_SBOX_BASE_ADDRESS +
+               MIC_X100_SBOX_SPAD0 + idx * 4);
+}
+
+/**
+ * __mic_send_intr - Send interrupt to Host.
+ * @mdev: pointer to mic_device instance
+ * @doorbell: Doorbell number.
+ */
+void mic_send_intr(struct mic_device *mdev, int doorbell)
+{
+       struct mic_mw *mw = &mdev->mmio;
+
+       if (doorbell > MIC_X100_MAX_DOORBELL_IDX)
+               return;
+       /* Ensure that the interrupt is ordered w.r.t previous stores. */
+       wmb();
+       mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT,
+                      MIC_X100_SBOX_BASE_ADDRESS +
+                      (MIC_X100_SBOX_SDBIC0 + (4 * doorbell)));
+}
+
+/**
+ * mic_ack_interrupt - Device specific interrupt handling.
+ * @mdev: pointer to mic_device instance
+ *
+ * Returns: bitmask of doorbell events triggered.
+ */
+u32 mic_ack_interrupt(struct mic_device *mdev)
+{
+       return 0;
+}
+
+static inline int mic_get_sbox_irq(int db)
+{
+       return MIC_X100_IRQ_BASE + db;
+}
+
+static inline int mic_get_rdmasr_irq(int index)
+{
+       return  MIC_X100_RDMASR_IRQ_BASE + index;
+}
+
+/**
+ * mic_hw_intr_init - Initialize h/w specific interrupt
+ * information.
+ * @mdrv: pointer to mic_driver
+ */
+void mic_hw_intr_init(struct mic_driver *mdrv)
+{
+       mdrv->intr_info.num_intr = MIC_X100_NUM_SBOX_IRQ +
+                               MIC_X100_NUM_RDMASR_IRQ;
+}
+
+/**
+ * mic_db_to_irq - Retrieve irq number corresponding to a doorbell.
+ * @mdrv: pointer to mic_driver
+ * @db: The doorbell obtained for which the irq is needed. Doorbell
+ * may correspond to an sbox doorbell or an rdmasr index.
+ *
+ * Returns the irq corresponding to the doorbell.
+ */
+int mic_db_to_irq(struct mic_driver *mdrv, int db)
+{
+       int rdmasr_index;
+       if (db < MIC_X100_NUM_SBOX_IRQ) {
+               return mic_get_sbox_irq(db);
+       } else {
+               rdmasr_index = db - MIC_X100_NUM_SBOX_IRQ +
+                       MIC_X100_RDMASR_IRQ_BASE;
+               return mic_get_rdmasr_irq(rdmasr_index);
+       }
+}
+
+/*
+ * mic_card_map - Allocate virtual address for a remote memory region.
+ * @mdev: pointer to mic_device instance.
+ * @addr: Remote DMA address.
+ * @size: Size of the region.
+ *
+ * Returns: Virtual address backing the remote memory region.
+ */
+void __iomem *
+mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size)
+{
+       return ioremap(addr, size);
+}
+
+/*
+ * mic_card_unmap - Unmap the virtual address for a remote memory region.
+ * @mdev: pointer to mic_device instance.
+ * @addr: Virtual address for remote memory region.
+ *
+ * Returns: None.
+ */
+void mic_card_unmap(struct mic_device *mdev, void __iomem *addr)
+{
+       iounmap(addr);
+}
+
+static int __init mic_probe(struct platform_device *pdev)
+{
+       struct mic_driver *mdrv = &g_drv;
+       struct mic_device *mdev = &mdrv->mdev;
+       int rc = 0;
+
+       mdrv->dev = &pdev->dev;
+       snprintf(mdrv->name, sizeof(mic_driver_name), mic_driver_name);
+
+       mdev->mmio.pa = MIC_X100_MMIO_BASE;
+       mdev->mmio.len = MIC_X100_MMIO_LEN;
+       mdev->mmio.va = ioremap(MIC_X100_MMIO_BASE, MIC_X100_MMIO_LEN);
+       if (!mdev->mmio.va) {
+               dev_err(&pdev->dev, "Cannot remap MMIO BAR\n");
+               rc = -EIO;
+               goto done;
+       }
+       mic_hw_intr_init(mdrv);
+       rc = mic_driver_init(mdrv);
+       if (rc) {
+               dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc);
+               goto iounmap;
+       }
+done:
+       return rc;
+iounmap:
+       iounmap(mdev->mmio.va);
+       return rc;
+}
+
+static int mic_remove(struct platform_device *pdev)
+{
+       struct mic_driver *mdrv = &g_drv;
+       struct mic_device *mdev = &mdrv->mdev;
+
+       mic_driver_uninit(mdrv);
+       iounmap(mdev->mmio.va);
+       return 0;
+}
+
+static void mic_platform_shutdown(struct platform_device *pdev)
+{
+       mic_remove(pdev);
+}
+
+static struct platform_device mic_platform_dev = {
+       .name = mic_driver_name,
+       .id   = 0,
+       .num_resources = 0,
+};
+
+static struct platform_driver __refdata mic_platform_driver = {
+       .probe = mic_probe,
+       .remove = mic_remove,
+       .shutdown = mic_platform_shutdown,
+       .driver         = {
+               .name   = mic_driver_name,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init mic_init(void)
+{
+       int ret;
+       struct cpuinfo_x86 *c = &cpu_data(0);
+
+       if (!(c->x86 == 11 && c->x86_model == 1)) {
+               ret = -ENODEV;
+               pr_err("%s not running on X100 ret %d\n", __func__, ret);
+               goto done;
+       }
+
+       mic_init_card_debugfs();
+       ret = platform_device_register(&mic_platform_dev);
+       if (ret) {
+               pr_err("platform_device_register ret %d\n", ret);
+               goto cleanup_debugfs;
+       }
+       ret = platform_driver_register(&mic_platform_driver);
+       if (ret) {
+               pr_err("platform_driver_register ret %d\n", ret);
+               goto device_unregister;
+       }
+       return ret;
+
+device_unregister:
+       platform_device_unregister(&mic_platform_dev);
+cleanup_debugfs:
+       mic_exit_card_debugfs();
+done:
+       return ret;
+}
+
+static void __exit mic_exit(void)
+{
+       platform_driver_unregister(&mic_platform_driver);
+       platform_device_unregister(&mic_platform_dev);
+       mic_exit_card_debugfs();
+}
+
+module_init(mic_init);
+module_exit(mic_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel(R) MIC X100 Card driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/mic/card/mic_x100.h b/drivers/misc/mic/card/mic_x100.h
new file mode 100644 (file)
index 0000000..d66ea55
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#ifndef _MIC_X100_CARD_H_
+#define _MIC_X100_CARD_H_
+
+#define MIC_X100_MMIO_BASE 0x08007C0000ULL
+#define MIC_X100_MMIO_LEN 0x00020000ULL
+#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000ULL
+
+#define MIC_X100_SBOX_SPAD0 0x0000AB20
+#define MIC_X100_SBOX_SDBIC0 0x0000CC90
+#define MIC_X100_SBOX_SDBIC0_DBREQ_BIT 0x80000000
+#define MIC_X100_SBOX_RDMASR0  0x0000B180
+
+#define MIC_X100_MAX_DOORBELL_IDX 8
+
+#define MIC_X100_NUM_SBOX_IRQ 8
+#define MIC_X100_NUM_RDMASR_IRQ 8
+#define MIC_X100_SBOX_IRQ_BASE 0
+#define MIC_X100_RDMASR_IRQ_BASE 17
+
+#define MIC_X100_IRQ_BASE 26
+
+#endif
diff --git a/drivers/misc/mic/common/mic_dev.h b/drivers/misc/mic/common/mic_dev.h
new file mode 100644 (file)
index 0000000..92999c2
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC driver.
+ *
+ */
+#ifndef __MIC_DEV_H__
+#define __MIC_DEV_H__
+
+/**
+ * struct mic_mw - MIC memory window
+ *
+ * @pa: Base physical address.
+ * @va: Base ioremap'd virtual address.
+ * @len: Size of the memory window.
+ */
+struct mic_mw {
+       phys_addr_t pa;
+       void __iomem *va;
+       resource_size_t len;
+};
+
+/*
+ * Scratch pad register offsets used by the host to communicate
+ * device page DMA address to the card.
+ */
+#define MIC_DPLO_SPAD 14
+#define MIC_DPHI_SPAD 15
+
+/*
+ * These values are supposed to be in the config_change field of the
+ * device page when the host sends a config change interrupt to the card.
+ */
+#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1
+#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2
+
+#endif
diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile
new file mode 100644 (file)
index 0000000..c2197f9
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Makefile - Intel MIC Linux driver.
+# Copyright(c) 2013, Intel Corporation.
+#
+obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o
+mic_host-objs := mic_main.o
+mic_host-objs += mic_x100.o
+mic_host-objs += mic_sysfs.o
+mic_host-objs += mic_smpt.o
+mic_host-objs += mic_intr.o
+mic_host-objs += mic_boot.o
+mic_host-objs += mic_debugfs.o
+mic_host-objs += mic_fops.o
+mic_host-objs += mic_virtio.o
diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c
new file mode 100644 (file)
index 0000000..b079c65
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_smpt.h"
+#include "mic_virtio.h"
+
+/**
+ * mic_reset - Reset the MIC device.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_reset(struct mic_device *mdev)
+{
+       int i;
+
+#define MIC_RESET_TO (45)
+
+       INIT_COMPLETION(mdev->reset_wait);
+       mdev->ops->reset_fw_ready(mdev);
+       mdev->ops->reset(mdev);
+
+       for (i = 0; i < MIC_RESET_TO; i++) {
+               if (mdev->ops->is_fw_ready(mdev))
+                       goto done;
+               /*
+                * Resets typically take 10s of seconds to complete.
+                * Since an MMIO read is required to check if the
+                * firmware is ready or not, a 1 second delay works nicely.
+                */
+               msleep(1000);
+       }
+       mic_set_state(mdev, MIC_RESET_FAILED);
+done:
+       complete_all(&mdev->reset_wait);
+}
+
+/* Initialize the MIC bootparams */
+void mic_bootparam_init(struct mic_device *mdev)
+{
+       struct mic_bootparam *bootparam = mdev->dp;
+
+       bootparam->magic = MIC_MAGIC;
+       bootparam->c2h_shutdown_db = mdev->shutdown_db;
+       bootparam->h2c_shutdown_db = -1;
+       bootparam->h2c_config_db = -1;
+       bootparam->shutdown_status = 0;
+       bootparam->shutdown_card = 0;
+}
+
+/**
+ * mic_start - Start the MIC.
+ * @mdev: pointer to mic_device instance
+ * @buf: buffer containing boot string including firmware/ramdisk path.
+ *
+ * This function prepares an MIC for boot and initiates boot.
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+int mic_start(struct mic_device *mdev, const char *buf)
+{
+       int rc;
+       mutex_lock(&mdev->mic_mutex);
+retry:
+       if (MIC_OFFLINE != mdev->state) {
+               rc = -EINVAL;
+               goto unlock_ret;
+       }
+       if (!mdev->ops->is_fw_ready(mdev)) {
+               mic_reset(mdev);
+               /*
+                * The state will either be MIC_OFFLINE if the reset succeeded
+                * or MIC_RESET_FAILED if the firmware reset failed.
+                */
+               goto retry;
+       }
+       rc = mdev->ops->load_mic_fw(mdev, buf);
+       if (rc)
+               goto unlock_ret;
+       mic_smpt_restore(mdev);
+       mic_intr_restore(mdev);
+       mdev->intr_ops->enable_interrupts(mdev);
+       mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr);
+       mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32);
+       mdev->ops->send_firmware_intr(mdev);
+       mic_set_state(mdev, MIC_ONLINE);
+unlock_ret:
+       mutex_unlock(&mdev->mic_mutex);
+       return rc;
+}
+
+/**
+ * mic_stop - Prepare the MIC for reset and trigger reset.
+ * @mdev: pointer to mic_device instance
+ * @force: force a MIC to reset even if it is already offline.
+ *
+ * RETURNS: None.
+ */
+void mic_stop(struct mic_device *mdev, bool force)
+{
+       mutex_lock(&mdev->mic_mutex);
+       if (MIC_OFFLINE != mdev->state || force) {
+               mic_virtio_reset_devices(mdev);
+               mic_bootparam_init(mdev);
+               mic_reset(mdev);
+               if (MIC_RESET_FAILED == mdev->state)
+                       goto unlock;
+               mic_set_shutdown_status(mdev, MIC_NOP);
+               if (MIC_SUSPENDED != mdev->state)
+                       mic_set_state(mdev, MIC_OFFLINE);
+       }
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+}
+
+/**
+ * mic_shutdown - Initiate MIC shutdown.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: None.
+ */
+void mic_shutdown(struct mic_device *mdev)
+{
+       struct mic_bootparam *bootparam = mdev->dp;
+       s8 db = bootparam->h2c_shutdown_db;
+
+       mutex_lock(&mdev->mic_mutex);
+       if (MIC_ONLINE == mdev->state && db != -1) {
+               bootparam->shutdown_card = 1;
+               mdev->ops->send_intr(mdev, db);
+               mic_set_state(mdev, MIC_SHUTTING_DOWN);
+       }
+       mutex_unlock(&mdev->mic_mutex);
+}
+
+/**
+ * mic_shutdown_work - Handle shutdown interrupt from MIC.
+ * @work: The work structure.
+ *
+ * This work is scheduled whenever the host has received a shutdown
+ * interrupt from the MIC.
+ */
+void mic_shutdown_work(struct work_struct *work)
+{
+       struct mic_device *mdev = container_of(work, struct mic_device,
+                       shutdown_work);
+       struct mic_bootparam *bootparam = mdev->dp;
+
+       mutex_lock(&mdev->mic_mutex);
+       mic_set_shutdown_status(mdev, bootparam->shutdown_status);
+       bootparam->shutdown_status = 0;
+
+       /*
+        * if state is MIC_SUSPENDED, OSPM suspend is in progress. We do not
+        * change the state here so as to prevent users from booting the card
+        * during and after the suspend operation.
+        */
+       if (MIC_SHUTTING_DOWN != mdev->state &&
+           MIC_SUSPENDED != mdev->state)
+               mic_set_state(mdev, MIC_SHUTTING_DOWN);
+       mutex_unlock(&mdev->mic_mutex);
+}
+
+/**
+ * mic_reset_trigger_work - Trigger MIC reset.
+ * @work: The work structure.
+ *
+ * This work is scheduled whenever the host wants to reset the MIC.
+ */
+void mic_reset_trigger_work(struct work_struct *work)
+{
+       struct mic_device *mdev = container_of(work, struct mic_device,
+                       reset_trigger_work);
+
+       mic_stop(mdev, false);
+}
+
+/**
+ * mic_complete_resume - Complete MIC Resume after an OSPM suspend/hibernate
+ * event.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: None.
+ */
+void mic_complete_resume(struct mic_device *mdev)
+{
+       if (mdev->state != MIC_SUSPENDED) {
+               dev_warn(mdev->sdev->parent, "state %d should be %d\n",
+                        mdev->state, MIC_SUSPENDED);
+               return;
+       }
+
+       /* Make sure firmware is ready */
+       if (!mdev->ops->is_fw_ready(mdev))
+               mic_stop(mdev, true);
+
+       mutex_lock(&mdev->mic_mutex);
+       mic_set_state(mdev, MIC_OFFLINE);
+       mutex_unlock(&mdev->mic_mutex);
+}
+
+/**
+ * mic_prepare_suspend - Handle suspend notification for the MIC device.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: None.
+ */
+void mic_prepare_suspend(struct mic_device *mdev)
+{
+       int rc;
+
+#define MIC_SUSPEND_TIMEOUT (60 * HZ)
+
+       mutex_lock(&mdev->mic_mutex);
+       switch (mdev->state) {
+       case MIC_OFFLINE:
+               /*
+                * Card is already offline. Set state to MIC_SUSPENDED
+                * to prevent users from booting the card.
+                */
+               mic_set_state(mdev, MIC_SUSPENDED);
+               mutex_unlock(&mdev->mic_mutex);
+               break;
+       case MIC_ONLINE:
+               /*
+                * Card is online. Set state to MIC_SUSPENDING and notify
+                * MIC user space daemon which will issue card
+                * shutdown and reset.
+                */
+               mic_set_state(mdev, MIC_SUSPENDING);
+               mutex_unlock(&mdev->mic_mutex);
+               rc = wait_for_completion_timeout(&mdev->reset_wait,
+                                               MIC_SUSPEND_TIMEOUT);
+               /* Force reset the card if the shutdown completion timed out */
+               if (!rc) {
+                       mutex_lock(&mdev->mic_mutex);
+                       mic_set_state(mdev, MIC_SUSPENDED);
+                       mutex_unlock(&mdev->mic_mutex);
+                       mic_stop(mdev, true);
+               }
+               break;
+       case MIC_SHUTTING_DOWN:
+               /*
+                * Card is shutting down. Set state to MIC_SUSPENDED
+                * to prevent further boot of the card.
+                */
+               mic_set_state(mdev, MIC_SUSPENDED);
+               mutex_unlock(&mdev->mic_mutex);
+               rc = wait_for_completion_timeout(&mdev->reset_wait,
+                                               MIC_SUSPEND_TIMEOUT);
+               /* Force reset the card if the shutdown completion timed out */
+               if (!rc)
+                       mic_stop(mdev, true);
+               break;
+       default:
+               mutex_unlock(&mdev->mic_mutex);
+               break;
+       }
+}
+
+/**
+ * mic_suspend - Initiate MIC suspend. Suspend merely issues card shutdown.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: None.
+ */
+void mic_suspend(struct mic_device *mdev)
+{
+       struct mic_bootparam *bootparam = mdev->dp;
+       s8 db = bootparam->h2c_shutdown_db;
+
+       mutex_lock(&mdev->mic_mutex);
+       if (MIC_SUSPENDING == mdev->state && db != -1) {
+               bootparam->shutdown_card = 1;
+               mdev->ops->send_intr(mdev, db);
+               mic_set_state(mdev, MIC_SUSPENDED);
+       }
+       mutex_unlock(&mdev->mic_mutex);
+}
diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c
new file mode 100644 (file)
index 0000000..028ba5d
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/debugfs.h>
+#include <linux/pci.h>
+#include <linux/seq_file.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_smpt.h"
+#include "mic_virtio.h"
+
+/* Debugfs parent dir */
+static struct dentry *mic_dbg;
+
+/**
+ * mic_log_buf_show - Display MIC kernel log buffer.
+ *
+ * log_buf addr/len is read from System.map by user space
+ * and populated in sysfs entries.
+ */
+static int mic_log_buf_show(struct seq_file *s, void *unused)
+{
+       void __iomem *log_buf_va;
+       int __iomem *log_buf_len_va;
+       struct mic_device *mdev = s->private;
+       void *kva;
+       int size;
+       unsigned long aper_offset;
+
+       if (!mdev || !mdev->log_buf_addr || !mdev->log_buf_len)
+               goto done;
+       /*
+        * Card kernel will never be relocated and any kernel text/data mapping
+        * can be translated to phys address by subtracting __START_KERNEL_map.
+        */
+       aper_offset = (unsigned long)mdev->log_buf_len - __START_KERNEL_map;
+       log_buf_len_va = mdev->aper.va + aper_offset;
+       aper_offset = (unsigned long)mdev->log_buf_addr - __START_KERNEL_map;
+       log_buf_va = mdev->aper.va + aper_offset;
+       size = ioread32(log_buf_len_va);
+
+       kva = kmalloc(size, GFP_KERNEL);
+       if (!kva)
+               goto done;
+       mutex_lock(&mdev->mic_mutex);
+       memcpy_fromio(kva, log_buf_va, size);
+       switch (mdev->state) {
+       case MIC_ONLINE:
+               /* Fall through */
+       case MIC_SHUTTING_DOWN:
+               seq_write(s, kva, size);
+               break;
+       default:
+               break;
+       }
+       mutex_unlock(&mdev->mic_mutex);
+       kfree(kva);
+done:
+       return 0;
+}
+
+static int mic_log_buf_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_log_buf_show, inode->i_private);
+}
+
+static int mic_log_buf_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations log_buf_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_log_buf_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_log_buf_release
+};
+
+static int mic_smpt_show(struct seq_file *s, void *pos)
+{
+       int i;
+       struct mic_device *mdev = s->private;
+       unsigned long flags;
+
+       seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n",
+                  mdev->id, "SMPT entry", "SW DMA addr", "RefCount");
+       seq_puts(s, "====================================================\n");
+
+       if (mdev->smpt) {
+               struct mic_smpt_info *smpt_info = mdev->smpt;
+               spin_lock_irqsave(&smpt_info->smpt_lock, flags);
+               for (i = 0; i < smpt_info->info.num_reg; i++) {
+                       seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n",
+                                  " ",  i, smpt_info->entry[i].dma_addr,
+                                  smpt_info->entry[i].ref_count);
+               }
+               spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
+       }
+       seq_puts(s, "====================================================\n");
+       return 0;
+}
+
+static int mic_smpt_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_smpt_show, inode->i_private);
+}
+
+static int mic_smpt_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations smpt_file_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_smpt_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_smpt_debug_release
+};
+
+static int mic_soft_reset_show(struct seq_file *s, void *pos)
+{
+       struct mic_device *mdev = s->private;
+
+       mic_stop(mdev, true);
+       return 0;
+}
+
+static int mic_soft_reset_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_soft_reset_show, inode->i_private);
+}
+
+static int mic_soft_reset_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations soft_reset_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_soft_reset_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_soft_reset_debug_release
+};
+
+static int mic_post_code_show(struct seq_file *s, void *pos)
+{
+       struct mic_device *mdev = s->private;
+       u32 reg = mdev->ops->get_postcode(mdev);
+
+       seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff);
+       return 0;
+}
+
+static int mic_post_code_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_post_code_show, inode->i_private);
+}
+
+static int mic_post_code_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations post_code_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_post_code_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_post_code_debug_release
+};
+
+static int mic_dp_show(struct seq_file *s, void *pos)
+{
+       struct mic_device *mdev = s->private;
+       struct mic_device_desc *d;
+       struct mic_device_ctrl *dc;
+       struct mic_vqconfig *vqconfig;
+       __u32 *features;
+       __u8 *config;
+       struct mic_bootparam *bootparam = mdev->dp;
+       int i, j;
+
+       seq_printf(s, "Bootparam: magic 0x%x\n",
+                  bootparam->magic);
+       seq_printf(s, "Bootparam: h2c_shutdown_db %d\n",
+                  bootparam->h2c_shutdown_db);
+       seq_printf(s, "Bootparam: h2c_config_db %d\n",
+                  bootparam->h2c_config_db);
+       seq_printf(s, "Bootparam: c2h_shutdown_db %d\n",
+                  bootparam->c2h_shutdown_db);
+       seq_printf(s, "Bootparam: shutdown_status %d\n",
+                  bootparam->shutdown_status);
+       seq_printf(s, "Bootparam: shutdown_card %d\n",
+                  bootparam->shutdown_card);
+
+       for (i = sizeof(*bootparam); i < MIC_DP_SIZE;
+            i += mic_total_desc_size(d)) {
+               d = mdev->dp + i;
+               dc = (void *)d + mic_aligned_desc_size(d);
+
+               /* end of list */
+               if (d->type == 0)
+                       break;
+
+               if (d->type == -1)
+                       continue;
+
+               seq_printf(s, "Type %d ", d->type);
+               seq_printf(s, "Num VQ %d ", d->num_vq);
+               seq_printf(s, "Feature Len %d\n", d->feature_len);
+               seq_printf(s, "Config Len %d ", d->config_len);
+               seq_printf(s, "Shutdown Status %d\n", d->status);
+
+               for (j = 0; j < d->num_vq; j++) {
+                       vqconfig = mic_vq_config(d) + j;
+                       seq_printf(s, "vqconfig[%d]: ", j);
+                       seq_printf(s, "address 0x%llx ", vqconfig->address);
+                       seq_printf(s, "num %d ", vqconfig->num);
+                       seq_printf(s, "used address 0x%llx\n",
+                                  vqconfig->used_address);
+               }
+
+               features = (__u32 *)mic_vq_features(d);
+               seq_printf(s, "Features: Host 0x%x ", features[0]);
+               seq_printf(s, "Guest 0x%x\n", features[1]);
+
+               config = mic_vq_configspace(d);
+               for (j = 0; j < d->config_len; j++)
+                       seq_printf(s, "config[%d]=%d\n", j, config[j]);
+
+               seq_puts(s, "Device control:\n");
+               seq_printf(s, "Config Change %d ", dc->config_change);
+               seq_printf(s, "Vdev reset %d\n", dc->vdev_reset);
+               seq_printf(s, "Guest Ack %d ", dc->guest_ack);
+               seq_printf(s, "Host ack %d\n", dc->host_ack);
+               seq_printf(s, "Used address updated %d ",
+                          dc->used_address_updated);
+               seq_printf(s, "Vdev 0x%llx\n", dc->vdev);
+               seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db);
+               seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db);
+       }
+
+       return 0;
+}
+
+static int mic_dp_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_dp_show, inode->i_private);
+}
+
+static int mic_dp_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations dp_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_dp_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_dp_debug_release
+};
+
+static int mic_vdev_info_show(struct seq_file *s, void *unused)
+{
+       struct mic_device *mdev = s->private;
+       struct list_head *pos, *tmp;
+       struct mic_vdev *mvdev;
+       int i, j;
+
+       mutex_lock(&mdev->mic_mutex);
+       list_for_each_safe(pos, tmp, &mdev->vdev_list) {
+               mvdev = list_entry(pos, struct mic_vdev, list);
+               seq_printf(s, "VDEV type %d state %s in %ld out %ld\n",
+                          mvdev->virtio_id,
+                          mic_vdevup(mvdev) ? "UP" : "DOWN",
+                          mvdev->in_bytes,
+                          mvdev->out_bytes);
+               for (i = 0; i < MIC_MAX_VRINGS; i++) {
+                       struct vring_desc *desc;
+                       struct vring_avail *avail;
+                       struct vring_used *used;
+                       struct mic_vringh *mvr = &mvdev->mvr[i];
+                       struct vringh *vrh = &mvr->vrh;
+                       int num = vrh->vring.num;
+                       if (!num)
+                               continue;
+                       desc = vrh->vring.desc;
+                       seq_printf(s, "vring i %d avail_idx %d",
+                                  i, mvr->vring.info->avail_idx & (num - 1));
+                       seq_printf(s, " vring i %d avail_idx %d\n",
+                                  i, mvr->vring.info->avail_idx);
+                       seq_printf(s, "vrh i %d weak_barriers %d",
+                                  i, vrh->weak_barriers);
+                       seq_printf(s, " last_avail_idx %d last_used_idx %d",
+                                  vrh->last_avail_idx, vrh->last_used_idx);
+                       seq_printf(s, " completed %d\n", vrh->completed);
+                       for (j = 0; j < num; j++) {
+                               seq_printf(s, "desc[%d] addr 0x%llx len %d",
+                                          j, desc->addr, desc->len);
+                               seq_printf(s, " flags 0x%x next %d\n",
+                                          desc->flags, desc->next);
+                               desc++;
+                       }
+                       avail = vrh->vring.avail;
+                       seq_printf(s, "avail flags 0x%x idx %d\n",
+                                  avail->flags, avail->idx & (num - 1));
+                       seq_printf(s, "avail flags 0x%x idx %d\n",
+                                  avail->flags, avail->idx);
+                       for (j = 0; j < num; j++)
+                               seq_printf(s, "avail ring[%d] %d\n",
+                                          j, avail->ring[j]);
+                       used = vrh->vring.used;
+                       seq_printf(s, "used flags 0x%x idx %d\n",
+                                  used->flags, used->idx & (num - 1));
+                       seq_printf(s, "used flags 0x%x idx %d\n",
+                                  used->flags, used->idx);
+                       for (j = 0; j < num; j++)
+                               seq_printf(s, "used ring[%d] id %d len %d\n",
+                                          j, used->ring[j].id,
+                                          used->ring[j].len);
+               }
+       }
+       mutex_unlock(&mdev->mic_mutex);
+
+       return 0;
+}
+
+static int mic_vdev_info_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_vdev_info_show, inode->i_private);
+}
+
+static int mic_vdev_info_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations vdev_info_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_vdev_info_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_vdev_info_debug_release
+};
+
+static int mic_msi_irq_info_show(struct seq_file *s, void *pos)
+{
+       struct mic_device *mdev  = s->private;
+       int reg;
+       int i, j;
+       u16 entry;
+       u16 vector;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       if (pci_dev_msi_enabled(pdev)) {
+               for (i = 0; i < mdev->irq_info.num_vectors; i++) {
+                       if (pdev->msix_enabled) {
+                               entry = mdev->irq_info.msix_entries[i].entry;
+                               vector = mdev->irq_info.msix_entries[i].vector;
+                       } else {
+                               entry = 0;
+                               vector = pdev->irq;
+                       }
+
+                       reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry);
+
+                       seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n",
+                                  "IRQ:", vector, "Entry:", entry, i, reg);
+
+                       seq_printf(s, "%-10s", "offset:");
+                       for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--)
+                               seq_printf(s, "%4d ", j);
+                       seq_puts(s, "\n");
+
+
+                       seq_printf(s, "%-10s", "count:");
+                       for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--)
+                               seq_printf(s, "%4d ",
+                                          (mdev->irq_info.mic_msi_map[i] &
+                                          BIT(j)) ? 1 : 0);
+                       seq_puts(s, "\n\n");
+               }
+       } else {
+               seq_puts(s, "MSI/MSIx interrupts not enabled\n");
+       }
+
+       return 0;
+}
+
+static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_msi_irq_info_show, inode->i_private);
+}
+
+static int
+mic_msi_irq_info_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations msi_irq_info_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_msi_irq_info_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_msi_irq_info_debug_release
+};
+
+/**
+ * mic_create_debug_dir - Initialize MIC debugfs entries.
+ */
+void mic_create_debug_dir(struct mic_device *mdev)
+{
+       if (!mic_dbg)
+               return;
+
+       mdev->dbg_dir = debugfs_create_dir(dev_name(mdev->sdev), mic_dbg);
+       if (!mdev->dbg_dir)
+               return;
+
+       debugfs_create_file("log_buf", 0444, mdev->dbg_dir, mdev, &log_buf_ops);
+
+       debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, &smpt_file_ops);
+
+       debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, mdev,
+                           &soft_reset_ops);
+
+       debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev,
+                           &post_code_ops);
+
+       debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops);
+
+       debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev,
+                           &vdev_info_ops);
+
+       debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev,
+                           &msi_irq_info_ops);
+}
+
+/**
+ * mic_delete_debug_dir - Uninitialize MIC debugfs entries.
+ */
+void mic_delete_debug_dir(struct mic_device *mdev)
+{
+       if (!mdev->dbg_dir)
+               return;
+
+       debugfs_remove_recursive(mdev->dbg_dir);
+}
+
+/**
+ * mic_init_debugfs - Initialize global debugfs entry.
+ */
+void __init mic_init_debugfs(void)
+{
+       mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
+       if (!mic_dbg)
+               pr_err("can't create debugfs dir\n");
+}
+
+/**
+ * mic_exit_debugfs - Uninitialize global debugfs entry
+ */
+void mic_exit_debugfs(void)
+{
+       debugfs_remove(mic_dbg);
+}
diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h
new file mode 100644 (file)
index 0000000..3574cc3
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_DEVICE_H_
+#define _MIC_DEVICE_H_
+
+#include <linux/cdev.h>
+#include <linux/idr.h>
+#include <linux/notifier.h>
+
+#include "mic_intr.h"
+
+/* The maximum number of MIC devices supported in a single host system. */
+#define MIC_MAX_NUM_DEVS 256
+
+/**
+ * enum mic_hw_family - The hardware family to which a device belongs.
+ */
+enum mic_hw_family {
+       MIC_FAMILY_X100 = 0,
+       MIC_FAMILY_UNKNOWN
+};
+
+/**
+ * enum mic_stepping - MIC stepping ids.
+ */
+enum mic_stepping {
+       MIC_A0_STEP = 0x0,
+       MIC_B0_STEP = 0x10,
+       MIC_B1_STEP = 0x11,
+       MIC_C0_STEP = 0x20,
+};
+
+/**
+ * struct mic_device -  MIC device information for each card.
+ *
+ * @mmio: MMIO bar information.
+ * @aper: Aperture bar information.
+ * @family: The MIC family to which this device belongs.
+ * @ops: MIC HW specific operations.
+ * @id: The unique device id for this MIC device.
+ * @stepping: Stepping ID.
+ * @attr_group: Pointer to list of sysfs attribute groups.
+ * @sdev: Device for sysfs entries.
+ * @mic_mutex: Mutex for synchronizing access to mic_device.
+ * @intr_ops: HW specific interrupt operations.
+ * @smpt_ops: Hardware specific SMPT operations.
+ * @smpt: MIC SMPT information.
+ * @intr_info: H/W specific interrupt information.
+ * @irq_info: The OS specific irq information
+ * @dbg_dir: debugfs directory of this MIC device.
+ * @cmdline: Kernel command line.
+ * @firmware: Firmware file name.
+ * @ramdisk: Ramdisk file name.
+ * @bootmode: Boot mode i.e. "linux" or "elf" for flash updates.
+ * @bootaddr: MIC boot address.
+ * @reset_trigger_work: Work for triggering reset requests.
+ * @shutdown_work: Work for handling shutdown interrupts.
+ * @state: MIC state.
+ * @shutdown_status: MIC status reported by card for shutdown/crashes.
+ * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes.
+ * @reset_wait: Waitqueue for sleeping while reset completes.
+ * @log_buf_addr: Log buffer address for MIC.
+ * @log_buf_len: Log buffer length address for MIC.
+ * @dp: virtio device page
+ * @dp_dma_addr: virtio device page DMA address.
+ * @shutdown_db: shutdown doorbell.
+ * @shutdown_cookie: shutdown cookie.
+ * @cdev: Character device for MIC.
+ * @vdev_list: list of virtio devices.
+ * @pm_notifier: Handles PM notifications from the OS.
+ */
+struct mic_device {
+       struct mic_mw mmio;
+       struct mic_mw aper;
+       enum mic_hw_family family;
+       struct mic_hw_ops *ops;
+       int id;
+       enum mic_stepping stepping;
+       const struct attribute_group **attr_group;
+       struct device *sdev;
+       struct mutex mic_mutex;
+       struct mic_hw_intr_ops *intr_ops;
+       struct mic_smpt_ops *smpt_ops;
+       struct mic_smpt_info *smpt;
+       struct mic_intr_info *intr_info;
+       struct mic_irq_info irq_info;
+       struct dentry *dbg_dir;
+       char *cmdline;
+       char *firmware;
+       char *ramdisk;
+       char *bootmode;
+       u32 bootaddr;
+       struct work_struct reset_trigger_work;
+       struct work_struct shutdown_work;
+       u8 state;
+       u8 shutdown_status;
+       struct sysfs_dirent *state_sysfs;
+       struct completion reset_wait;
+       void *log_buf_addr;
+       int *log_buf_len;
+       void *dp;
+       dma_addr_t dp_dma_addr;
+       int shutdown_db;
+       struct mic_irq *shutdown_cookie;
+       struct cdev cdev;
+       struct list_head vdev_list;
+       struct notifier_block pm_notifier;
+};
+
+/**
+ * struct mic_hw_ops - MIC HW specific operations.
+ * @aper_bar: Aperture bar resource number.
+ * @mmio_bar: MMIO bar resource number.
+ * @read_spad: Read from scratch pad register.
+ * @write_spad: Write to scratch pad register.
+ * @send_intr: Send an interrupt for a particular doorbell on the card.
+ * @ack_interrupt: Hardware specific operations to ack the h/w on
+ * receipt of an interrupt.
+ * @reset: Reset the remote processor.
+ * @reset_fw_ready: Reset firmware ready field.
+ * @is_fw_ready: Check if firmware is ready for OS download.
+ * @send_firmware_intr: Send an interrupt to the card firmware.
+ * @load_mic_fw: Load firmware segments required to boot the card
+ * into card memory. This includes the kernel, command line, ramdisk etc.
+ * @get_postcode: Get post code status from firmware.
+ */
+struct mic_hw_ops {
+       u8 aper_bar;
+       u8 mmio_bar;
+       u32 (*read_spad)(struct mic_device *mdev, unsigned int idx);
+       void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val);
+       void (*send_intr)(struct mic_device *mdev, int doorbell);
+       u32 (*ack_interrupt)(struct mic_device *mdev);
+       void (*reset)(struct mic_device *mdev);
+       void (*reset_fw_ready)(struct mic_device *mdev);
+       bool (*is_fw_ready)(struct mic_device *mdev);
+       void (*send_firmware_intr)(struct mic_device *mdev);
+       int (*load_mic_fw)(struct mic_device *mdev, const char *buf);
+       u32 (*get_postcode)(struct mic_device *mdev);
+};
+
+/**
+ * mic_mmio_read - read from an MMIO register.
+ * @mw: MMIO register base virtual address.
+ * @offset: register offset.
+ *
+ * RETURNS: register value.
+ */
+static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset)
+{
+       return ioread32(mw->va + offset);
+}
+
+/**
+ * mic_mmio_write - write to an MMIO register.
+ * @mw: MMIO register base virtual address.
+ * @val: the data value to put into the register
+ * @offset: register offset.
+ *
+ * RETURNS: none.
+ */
+static inline void
+mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset)
+{
+       iowrite32(val, mw->va + offset);
+}
+
+void mic_sysfs_init(struct mic_device *mdev);
+int mic_start(struct mic_device *mdev, const char *buf);
+void mic_stop(struct mic_device *mdev, bool force);
+void mic_shutdown(struct mic_device *mdev);
+void mic_reset_delayed_work(struct work_struct *work);
+void mic_reset_trigger_work(struct work_struct *work);
+void mic_shutdown_work(struct work_struct *work);
+void mic_bootparam_init(struct mic_device *mdev);
+void mic_set_state(struct mic_device *mdev, u8 state);
+void mic_set_shutdown_status(struct mic_device *mdev, u8 status);
+void mic_create_debug_dir(struct mic_device *dev);
+void mic_delete_debug_dir(struct mic_device *dev);
+void __init mic_init_debugfs(void);
+void mic_exit_debugfs(void);
+void mic_prepare_suspend(struct mic_device *mdev);
+void mic_complete_resume(struct mic_device *mdev);
+void mic_suspend(struct mic_device *mdev);
+#endif
diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c
new file mode 100644 (file)
index 0000000..85776d7
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/poll.h>
+#include <linux/pci.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_fops.h"
+#include "mic_virtio.h"
+
+int mic_open(struct inode *inode, struct file *f)
+{
+       struct mic_vdev *mvdev;
+       struct mic_device *mdev = container_of(inode->i_cdev,
+               struct mic_device, cdev);
+
+       mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL);
+       if (!mvdev)
+               return -ENOMEM;
+
+       init_waitqueue_head(&mvdev->waitq);
+       INIT_LIST_HEAD(&mvdev->list);
+       mvdev->mdev = mdev;
+       mvdev->virtio_id = -1;
+
+       f->private_data = mvdev;
+       return 0;
+}
+
+int mic_release(struct inode *inode, struct file *f)
+{
+       struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
+
+       if (-1 != mvdev->virtio_id)
+               mic_virtio_del_device(mvdev);
+       f->private_data = NULL;
+       kfree(mvdev);
+       return 0;
+}
+
+long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+       struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
+       void __user *argp = (void __user *)arg;
+       int ret;
+
+       switch (cmd) {
+       case MIC_VIRTIO_ADD_DEVICE:
+       {
+               ret = mic_virtio_add_device(mvdev, argp);
+               if (ret < 0) {
+                       dev_err(mic_dev(mvdev),
+                               "%s %d errno ret %d\n",
+                               __func__, __LINE__, ret);
+                       return ret;
+               }
+               break;
+       }
+       case MIC_VIRTIO_COPY_DESC:
+       {
+               struct mic_copy_desc copy;
+
+               ret = mic_vdev_inited(mvdev);
+               if (ret)
+                       return ret;
+
+               if (copy_from_user(&copy, argp, sizeof(copy)))
+                       return -EFAULT;
+
+               dev_dbg(mic_dev(mvdev),
+                       "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n",
+                       __func__, __LINE__, copy.iovcnt, copy.vr_idx,
+                       copy.update_used);
+
+               ret = mic_virtio_copy_desc(mvdev, &copy);
+               if (ret < 0) {
+                       dev_err(mic_dev(mvdev),
+                               "%s %d errno ret %d\n",
+                               __func__, __LINE__, ret);
+                       return ret;
+               }
+               if (copy_to_user(
+                       &((struct mic_copy_desc __user *)argp)->out_len,
+                       &copy.out_len, sizeof(copy.out_len))) {
+                       dev_err(mic_dev(mvdev), "%s %d errno ret %d\n",
+                               __func__, __LINE__, -EFAULT);
+                       return -EFAULT;
+               }
+               break;
+       }
+       case MIC_VIRTIO_CONFIG_CHANGE:
+       {
+               ret = mic_vdev_inited(mvdev);
+               if (ret)
+                       return ret;
+
+               ret = mic_virtio_config_change(mvdev, argp);
+               if (ret < 0) {
+                       dev_err(mic_dev(mvdev),
+                               "%s %d errno ret %d\n",
+                               __func__, __LINE__, ret);
+                       return ret;
+               }
+               break;
+       }
+       default:
+               return -ENOIOCTLCMD;
+       };
+       return 0;
+}
+
+/*
+ * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and
+ * not when previously enqueued buffers may be available. This means that
+ * in the card->host (TX) path, when userspace is unblocked by poll it
+ * must drain all available descriptors or it can stall.
+ */
+unsigned int mic_poll(struct file *f, poll_table *wait)
+{
+       struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
+       int mask = 0;
+
+       poll_wait(f, &mvdev->waitq, wait);
+
+       if (mic_vdev_inited(mvdev)) {
+               mask = POLLERR;
+       } else if (mvdev->poll_wake) {
+               mvdev->poll_wake = 0;
+               mask = POLLIN | POLLOUT;
+       }
+
+       return mask;
+}
+
+static inline int
+mic_query_offset(struct mic_vdev *mvdev, unsigned long offset,
+                unsigned long *size, unsigned long *pa)
+{
+       struct mic_device *mdev = mvdev->mdev;
+       unsigned long start = MIC_DP_SIZE;
+       int i;
+
+       /*
+        * MMAP interface is as follows:
+        * offset                               region
+        * 0x0                                  virtio device_page
+        * 0x1000                               first vring
+        * 0x1000 + size of 1st vring           second vring
+        * ....
+        */
+       if (!offset) {
+               *pa = virt_to_phys(mdev->dp);
+               *size = MIC_DP_SIZE;
+               return 0;
+       }
+
+       for (i = 0; i < mvdev->dd->num_vq; i++) {
+               struct mic_vringh *mvr = &mvdev->mvr[i];
+               if (offset == start) {
+                       *pa = virt_to_phys(mvr->vring.va);
+                       *size = mvr->vring.len;
+                       return 0;
+               }
+               start += mvr->vring.len;
+       }
+       return -1;
+}
+
+/*
+ * Maps the device page and virtio rings to user space for readonly access.
+ */
+int
+mic_mmap(struct file *f, struct vm_area_struct *vma)
+{
+       struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+       unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size;
+       int i, err;
+
+       err = mic_vdev_inited(mvdev);
+       if (err)
+               return err;
+
+       if (vma->vm_flags & VM_WRITE)
+               return -EACCES;
+
+       while (size_rem) {
+               i = mic_query_offset(mvdev, offset, &size, &pa);
+               if (i < 0)
+                       return -EINVAL;
+               err = remap_pfn_range(vma, vma->vm_start + offset,
+                       pa >> PAGE_SHIFT, size, vma->vm_page_prot);
+               if (err)
+                       return err;
+               dev_dbg(mic_dev(mvdev),
+                       "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n",
+                       __func__, __LINE__, mvdev->virtio_id, size, offset,
+                       pa, vma->vm_start + offset);
+               size_rem -= size;
+               offset += size;
+       }
+       return 0;
+}
diff --git a/drivers/misc/mic/host/mic_fops.h b/drivers/misc/mic/host/mic_fops.h
new file mode 100644 (file)
index 0000000..dc3893d
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_FOPS_H_
+#define _MIC_FOPS_H_
+
+int mic_open(struct inode *inode, struct file *filp);
+int mic_release(struct inode *inode, struct file *filp);
+ssize_t mic_read(struct file *filp, char __user *buf,
+                       size_t count, loff_t *pos);
+long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+int mic_mmap(struct file *f, struct vm_area_struct *vma);
+unsigned int mic_poll(struct file *f, poll_table *wait);
+
+#endif
diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c
new file mode 100644 (file)
index 0000000..f9c29bc
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+
+/*
+ * mic_invoke_callback - Invoke callback functions registered for
+ * the corresponding source id.
+ *
+ * @mdev: pointer to the mic_device instance
+ * @idx: The interrupt source id.
+ *
+ * Returns none.
+ */
+static inline void mic_invoke_callback(struct mic_device *mdev, int idx)
+{
+       struct mic_intr_cb *intr_cb;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       spin_lock(&mdev->irq_info.mic_intr_lock);
+       list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list)
+               if (intr_cb->func)
+                       intr_cb->func(pdev->irq, intr_cb->data);
+       spin_unlock(&mdev->irq_info.mic_intr_lock);
+}
+
+/**
+ * mic_interrupt - Generic interrupt handler for
+ * MSI and INTx based interrupts.
+ */
+static irqreturn_t mic_interrupt(int irq, void *dev)
+{
+       struct mic_device *mdev = dev;
+       struct mic_intr_info *info = mdev->intr_info;
+       u32 mask;
+       int i;
+
+       mask = mdev->ops->ack_interrupt(mdev);
+       if (!mask)
+               return IRQ_NONE;
+
+       for (i = info->intr_start_idx[MIC_INTR_DB];
+                       i < info->intr_len[MIC_INTR_DB]; i++)
+               if (mask & BIT(i))
+                       mic_invoke_callback(mdev, i);
+
+       return IRQ_HANDLED;
+}
+
+/* Return the interrupt offset from the index. Index is 0 based. */
+static u16 mic_map_src_to_offset(struct mic_device *mdev,
+               int intr_src, enum mic_intr_type type)
+{
+       if (type >= MIC_NUM_INTR_TYPES)
+               return MIC_NUM_OFFSETS;
+       if (intr_src >= mdev->intr_info->intr_len[type])
+               return MIC_NUM_OFFSETS;
+
+       return mdev->intr_info->intr_start_idx[type] + intr_src;
+}
+
+/* Return next available msix_entry. */
+static struct msix_entry *mic_get_available_vector(struct mic_device *mdev)
+{
+       int i;
+       struct mic_irq_info *info = &mdev->irq_info;
+
+       for (i = 0; i < info->num_vectors; i++)
+               if (!info->mic_msi_map[i])
+                       return &info->msix_entries[i];
+       return NULL;
+}
+
+/**
+ * mic_register_intr_callback - Register a callback handler for the
+ * given source id.
+ *
+ * @mdev: pointer to the mic_device instance
+ * @idx: The source id to be registered.
+ * @func: The function to be called when the source id receives
+ * the interrupt.
+ * @data: Private data of the requester.
+ * Return the callback structure that was registered or an
+ * appropriate error on failure.
+ */
+static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev,
+                       u8 idx, irqreturn_t (*func) (int irq, void *dev),
+                       void *data)
+{
+       struct mic_intr_cb *intr_cb;
+       unsigned long flags;
+       int rc;
+       intr_cb = kmalloc(sizeof(*intr_cb), GFP_KERNEL);
+
+       if (!intr_cb)
+               return ERR_PTR(-ENOMEM);
+
+       intr_cb->func = func;
+       intr_cb->data = data;
+       intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida,
+               0, 0, GFP_KERNEL);
+       if (intr_cb->cb_id < 0) {
+               rc = intr_cb->cb_id;
+               goto ida_fail;
+       }
+
+       spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
+       list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]);
+       spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+
+       return intr_cb;
+ida_fail:
+       kfree(intr_cb);
+       return ERR_PTR(rc);
+}
+
+/**
+ * mic_unregister_intr_callback - Unregister the callback handler
+ * identified by its callback id.
+ *
+ * @mdev: pointer to the mic_device instance
+ * @idx: The callback structure id to be unregistered.
+ * Return the source id that was unregistered or MIC_NUM_OFFSETS if no
+ * such callback handler was found.
+ */
+static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx)
+{
+       struct list_head *pos, *tmp;
+       struct mic_intr_cb *intr_cb;
+       unsigned long flags;
+       int i;
+
+       for (i = 0;  i < MIC_NUM_OFFSETS; i++) {
+               spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
+               list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) {
+                       intr_cb = list_entry(pos, struct mic_intr_cb, list);
+                       if (intr_cb->cb_id == idx) {
+                               list_del(pos);
+                               ida_simple_remove(&mdev->irq_info.cb_ida,
+                                                 intr_cb->cb_id);
+                               kfree(intr_cb);
+                               spin_unlock_irqrestore(
+                                       &mdev->irq_info.mic_intr_lock, flags);
+                               return i;
+                       }
+               }
+               spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+       }
+       return MIC_NUM_OFFSETS;
+}
+
+/**
+ * mic_setup_msix - Initializes MSIx interrupts.
+ *
+ * @mdev: pointer to mic_device instance
+ *
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc, i;
+       int entry_size = sizeof(*mdev->irq_info.msix_entries);
+
+       mdev->irq_info.msix_entries = kmalloc_array(MIC_MIN_MSIX,
+                                                   entry_size, GFP_KERNEL);
+       if (!mdev->irq_info.msix_entries) {
+               rc = -ENOMEM;
+               goto err_nomem1;
+       }
+
+       for (i = 0; i < MIC_MIN_MSIX; i++)
+               mdev->irq_info.msix_entries[i].entry = i;
+
+       rc = pci_enable_msix(pdev, mdev->irq_info.msix_entries,
+               MIC_MIN_MSIX);
+       if (rc) {
+               dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc);
+               goto err_enable_msix;
+       }
+
+       mdev->irq_info.num_vectors = MIC_MIN_MSIX;
+       mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) *
+               mdev->irq_info.num_vectors), GFP_KERNEL);
+
+       if (!mdev->irq_info.mic_msi_map) {
+               rc = -ENOMEM;
+               goto err_nomem2;
+       }
+
+       dev_dbg(mdev->sdev->parent,
+               "%d MSIx irqs setup\n", mdev->irq_info.num_vectors);
+       return 0;
+err_nomem2:
+       pci_disable_msix(pdev);
+err_enable_msix:
+       kfree(mdev->irq_info.msix_entries);
+err_nomem1:
+       mdev->irq_info.num_vectors = 0;
+       return rc;
+}
+
+/**
+ * mic_setup_callbacks - Initialize data structures needed
+ * to handle callbacks.
+ *
+ * @mdev: pointer to mic_device instance
+ */
+static int mic_setup_callbacks(struct mic_device *mdev)
+{
+       int i;
+
+       mdev->irq_info.cb_list = kmalloc_array(MIC_NUM_OFFSETS,
+                                              sizeof(*mdev->irq_info.cb_list),
+                                              GFP_KERNEL);
+       if (!mdev->irq_info.cb_list)
+               return -ENOMEM;
+
+       for (i = 0; i < MIC_NUM_OFFSETS; i++)
+               INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]);
+       ida_init(&mdev->irq_info.cb_ida);
+       spin_lock_init(&mdev->irq_info.mic_intr_lock);
+       return 0;
+}
+
+/**
+ * mic_release_callbacks - Uninitialize data structures needed
+ * to handle callbacks.
+ *
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_release_callbacks(struct mic_device *mdev)
+{
+       unsigned long flags;
+       struct list_head *pos, *tmp;
+       struct mic_intr_cb *intr_cb;
+       int i;
+
+       for (i = 0; i < MIC_NUM_OFFSETS; i++) {
+               spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
+
+               if (list_empty(&mdev->irq_info.cb_list[i])) {
+                       spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock,
+                                              flags);
+                       break;
+               }
+
+               list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) {
+                       intr_cb = list_entry(pos, struct mic_intr_cb, list);
+                       list_del(pos);
+                       ida_simple_remove(&mdev->irq_info.cb_ida,
+                                         intr_cb->cb_id);
+                       kfree(intr_cb);
+               }
+               spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+       }
+       ida_destroy(&mdev->irq_info.cb_ida);
+       kfree(mdev->irq_info.cb_list);
+}
+
+/**
+ * mic_setup_msi - Initializes MSI interrupts.
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: PCI device structure
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc;
+
+       rc = pci_enable_msi(pdev);
+       if (rc) {
+               dev_dbg(&pdev->dev, "Error enabling MSI. rc = %d\n", rc);
+               return rc;
+       }
+
+       mdev->irq_info.num_vectors = 1;
+       mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) *
+               mdev->irq_info.num_vectors), GFP_KERNEL);
+
+       if (!mdev->irq_info.mic_msi_map) {
+               rc = -ENOMEM;
+               goto err_nomem1;
+       }
+
+       rc = mic_setup_callbacks(mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "Error setting up callbacks\n");
+               goto err_nomem2;
+       }
+
+       rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI interrupt\n");
+               goto err_irq_req_fail;
+       }
+
+       dev_dbg(&pdev->dev, "%d MSI irqs setup\n", mdev->irq_info.num_vectors);
+       return 0;
+err_irq_req_fail:
+       mic_release_callbacks(mdev);
+err_nomem2:
+       kfree(mdev->irq_info.mic_msi_map);
+err_nomem1:
+       pci_disable_msi(pdev);
+       mdev->irq_info.num_vectors = 0;
+       return rc;
+}
+
+/**
+ * mic_setup_intx - Initializes legacy interrupts.
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: PCI device structure
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc;
+
+       pci_msi_off(pdev);
+
+       /* Enable intx */
+       pci_intx(pdev, 1);
+       rc = mic_setup_callbacks(mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "Error setting up callbacks\n");
+               goto err_nomem;
+       }
+
+       rc = request_irq(pdev->irq, mic_interrupt,
+               IRQF_SHARED, "mic-intx", mdev);
+       if (rc)
+               goto err;
+
+       dev_dbg(&pdev->dev, "intx irq setup\n");
+       return 0;
+err:
+       mic_release_callbacks(mdev);
+err_nomem:
+       return rc;
+}
+
+/**
+ * mic_next_db - Retrieve the next doorbell interrupt source id.
+ * The id is picked sequentially from the available pool of
+ * doorlbell ids.
+ *
+ * @mdev: pointer to the mic_device instance.
+ *
+ * Returns the next doorbell interrupt source.
+ */
+int mic_next_db(struct mic_device *mdev)
+{
+       int next_db;
+
+       next_db = mdev->irq_info.next_avail_src %
+               mdev->intr_info->intr_len[MIC_INTR_DB];
+       mdev->irq_info.next_avail_src++;
+       return next_db;
+}
+
+#define COOKIE_ID_SHIFT 16
+#define GET_ENTRY(cookie) ((cookie) & 0xFFFF)
+#define GET_OFFSET(cookie) ((cookie) >> COOKIE_ID_SHIFT)
+#define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT)
+
+/**
+ * mic_request_irq - request an irq. mic_mutex needs
+ * to be held before calling this function.
+ *
+ * @mdev: pointer to mic_device instance
+ * @func: The callback function that handles the interrupt.
+ * The function needs to call ack_interrupts
+ * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts.
+ * @name: The ASCII name of the callee requesting the irq.
+ * @data: private data that is returned back when calling the
+ * function handler.
+ * @intr_src: The source id of the requester. Its the doorbell id
+ * for Doorbell interrupts and DMA channel id for DMA interrupts.
+ * @type: The type of interrupt. Values defined in mic_intr_type
+ *
+ * returns: The cookie that is transparent to the caller. Passed
+ * back when calling mic_free_irq. An appropriate error code
+ * is returned on failure. Caller needs to use IS_ERR(return_val)
+ * to check for failure and PTR_ERR(return_val) to obtained the
+ * error code.
+ *
+ */
+struct mic_irq *mic_request_irq(struct mic_device *mdev,
+       irqreturn_t (*func)(int irq, void *dev),
+       const char *name, void *data, int intr_src,
+       enum mic_intr_type type)
+{
+       u16 offset;
+       int rc = 0;
+       struct msix_entry *msix = NULL;
+       unsigned long cookie = 0;
+       u16 entry;
+       struct mic_intr_cb *intr_cb;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       offset = mic_map_src_to_offset(mdev, intr_src, type);
+       if (offset >= MIC_NUM_OFFSETS) {
+               dev_err(mdev->sdev->parent,
+                       "Error mapping index %d to a valid source id.\n",
+                       intr_src);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       if (mdev->irq_info.num_vectors > 1) {
+               msix = mic_get_available_vector(mdev);
+               if (!msix) {
+                       dev_err(mdev->sdev->parent,
+                               "No MSIx vectors available for use.\n");
+                       rc = -ENOSPC;
+                       goto err;
+               }
+
+               rc = request_irq(msix->vector, func, 0, name, data);
+               if (rc) {
+                       dev_dbg(mdev->sdev->parent,
+                               "request irq failed rc = %d\n", rc);
+                       goto err;
+               }
+               entry = msix->entry;
+               mdev->irq_info.mic_msi_map[entry] |= BIT(offset);
+               mdev->intr_ops->program_msi_to_src_map(mdev,
+                               entry, offset, true);
+               cookie = MK_COOKIE(entry, offset);
+               dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n",
+                       msix->vector, intr_src);
+       } else {
+               intr_cb = mic_register_intr_callback(mdev,
+                               offset, func, data);
+               if (IS_ERR(intr_cb)) {
+                       dev_err(mdev->sdev->parent,
+                               "No available callback entries for use\n");
+                       rc = PTR_ERR(intr_cb);
+                       goto err;
+               }
+
+               entry = 0;
+               if (pci_dev_msi_enabled(pdev)) {
+                       mdev->irq_info.mic_msi_map[entry] |= (1 << offset);
+                       mdev->intr_ops->program_msi_to_src_map(mdev,
+                               entry, offset, true);
+               }
+               cookie = MK_COOKIE(entry, intr_cb->cb_id);
+               dev_dbg(mdev->sdev->parent, "callback %d registered for src: %d\n",
+                       intr_cb->cb_id, intr_src);
+       }
+       return (struct mic_irq *)cookie;
+err:
+       return ERR_PTR(rc);
+}
+
+/**
+ * mic_free_irq - free irq. mic_mutex
+ *  needs to be held before calling this function.
+ *
+ * @mdev: pointer to mic_device instance
+ * @cookie: cookie obtained during a successful call to mic_request_irq
+ * @data: private data specified by the calling function during the
+ * mic_request_irq
+ *
+ * returns: none.
+ */
+void mic_free_irq(struct mic_device *mdev,
+       struct mic_irq *cookie, void *data)
+{
+       u32 offset;
+       u32 entry;
+       u8 src_id;
+       unsigned int irq;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       entry = GET_ENTRY((unsigned long)cookie);
+       offset = GET_OFFSET((unsigned long)cookie);
+       if (mdev->irq_info.num_vectors > 1) {
+               if (entry >= mdev->irq_info.num_vectors) {
+                       dev_warn(mdev->sdev->parent,
+                                "entry %d should be < num_irq %d\n",
+                               entry, mdev->irq_info.num_vectors);
+                       return;
+               }
+               irq = mdev->irq_info.msix_entries[entry].vector;
+               free_irq(irq, data);
+               mdev->irq_info.mic_msi_map[entry] &= ~(BIT(offset));
+               mdev->intr_ops->program_msi_to_src_map(mdev,
+                       entry, offset, false);
+
+               dev_dbg(mdev->sdev->parent, "irq: %d freed\n", irq);
+       } else {
+               irq = pdev->irq;
+               src_id = mic_unregister_intr_callback(mdev, offset);
+               if (src_id >= MIC_NUM_OFFSETS) {
+                       dev_warn(mdev->sdev->parent, "Error unregistering callback\n");
+                       return;
+               }
+               if (pci_dev_msi_enabled(pdev)) {
+                       mdev->irq_info.mic_msi_map[entry] &= ~(BIT(src_id));
+                       mdev->intr_ops->program_msi_to_src_map(mdev,
+                               entry, src_id, false);
+               }
+               dev_dbg(mdev->sdev->parent, "callback %d unregistered for src: %d\n",
+                       offset, src_id);
+       }
+}
+
+/**
+ * mic_setup_interrupts - Initializes interrupts.
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: PCI device structure
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc;
+
+       rc = mic_setup_msix(mdev, pdev);
+       if (!rc)
+               goto done;
+
+       rc = mic_setup_msi(mdev, pdev);
+       if (!rc)
+               goto done;
+
+       rc = mic_setup_intx(mdev, pdev);
+       if (rc) {
+               dev_err(mdev->sdev->parent, "no usable interrupts\n");
+               return rc;
+       }
+done:
+       mdev->intr_ops->enable_interrupts(mdev);
+       return 0;
+}
+
+/**
+ * mic_free_interrupts - Frees interrupts setup by mic_setup_interrupts
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: PCI device structure
+ *
+ * returns none.
+ */
+void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int i;
+
+       mdev->intr_ops->disable_interrupts(mdev);
+       if (mdev->irq_info.num_vectors > 1) {
+               for (i = 0; i < mdev->irq_info.num_vectors; i++) {
+                       if (mdev->irq_info.mic_msi_map[i])
+                               dev_warn(&pdev->dev, "irq %d may still be in use.\n",
+                                        mdev->irq_info.msix_entries[i].vector);
+               }
+               kfree(mdev->irq_info.mic_msi_map);
+               kfree(mdev->irq_info.msix_entries);
+               pci_disable_msix(pdev);
+       } else {
+               if (pci_dev_msi_enabled(pdev)) {
+                       free_irq(pdev->irq, mdev);
+                       kfree(mdev->irq_info.mic_msi_map);
+                       pci_disable_msi(pdev);
+               } else {
+                       free_irq(pdev->irq, mdev);
+               }
+               mic_release_callbacks(mdev);
+       }
+}
+
+/**
+ * mic_intr_restore - Restore MIC interrupt registers.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * Restore the interrupt registers to values previously
+ * stored in the SW data structures. mic_mutex needs to
+ * be held before calling this function.
+ *
+ * returns None.
+ */
+void mic_intr_restore(struct mic_device *mdev)
+{
+       int entry, offset;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       if (!pci_dev_msi_enabled(pdev))
+               return;
+
+       for (entry = 0; entry < mdev->irq_info.num_vectors; entry++) {
+               for (offset = 0; offset < MIC_NUM_OFFSETS; offset++) {
+                       if (mdev->irq_info.mic_msi_map[entry] & BIT(offset))
+                               mdev->intr_ops->program_msi_to_src_map(mdev,
+                                       entry, offset, true);
+               }
+       }
+}
diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h
new file mode 100644 (file)
index 0000000..6091aa9
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_INTR_H_
+#define _MIC_INTR_H_
+
+/*
+ * The minimum number of msix vectors required for normal operation.
+ * 3 for virtio network, console and block devices.
+ * 1 for card shutdown notifications.
+ */
+#define MIC_MIN_MSIX 4
+#define MIC_NUM_OFFSETS 32
+
+/**
+ * mic_intr_source - The type of source that will generate
+ * the interrupt.The number of types needs to be in sync with
+ * MIC_NUM_INTR_TYPES
+ *
+ * MIC_INTR_DB: The source is a doorbell
+ * MIC_INTR_DMA: The source is a DMA channel
+ * MIC_INTR_ERR: The source is an error interrupt e.g. SBOX ERR
+ * MIC_NUM_INTR_TYPES: Total number of interrupt sources.
+ */
+enum mic_intr_type {
+       MIC_INTR_DB = 0,
+       MIC_INTR_DMA,
+       MIC_INTR_ERR,
+       MIC_NUM_INTR_TYPES
+};
+
+/**
+ * struct mic_intr_info - Contains h/w specific interrupt sources
+ * information.
+ *
+ * @intr_start_idx: Contains the starting indexes of the
+ * interrupt types.
+ * @intr_len: Contains the length of the interrupt types.
+ */
+struct mic_intr_info {
+       u16 intr_start_idx[MIC_NUM_INTR_TYPES];
+       u16 intr_len[MIC_NUM_INTR_TYPES];
+};
+
+/**
+ * struct mic_irq_info - OS specific irq information
+ *
+ * @next_avail_src: next available doorbell that can be assigned.
+ * @msix_entries: msix entries allocated while setting up MSI-x
+ * @mic_msi_map: The MSI/MSI-x mapping information.
+ * @num_vectors: The number of MSI/MSI-x vectors that have been allocated.
+ * @cb_ida: callback ID allocator to track the callbacks registered.
+ * @mic_intr_lock: spinlock to protect the interrupt callback list.
+ * @cb_list: Array of callback lists one for each source.
+ */
+struct mic_irq_info {
+       int next_avail_src;
+       struct msix_entry *msix_entries;
+       u32 *mic_msi_map;
+       u16 num_vectors;
+       struct ida cb_ida;
+       spinlock_t mic_intr_lock;
+       struct list_head *cb_list;
+};
+
+/**
+ * struct mic_intr_cb - Interrupt callback structure.
+ *
+ * @func: The callback function
+ * @data: Private data of the requester.
+ * @cb_id: The callback id. Identifies this callback.
+ * @list: list head pointing to the next callback structure.
+ */
+struct mic_intr_cb {
+       irqreturn_t (*func) (int irq, void *data);
+       void *data;
+       int cb_id;
+       struct list_head list;
+};
+
+/**
+ * struct mic_irq - opaque pointer used as cookie
+ */
+struct mic_irq;
+
+/* Forward declaration */
+struct mic_device;
+
+/**
+ * struct mic_hw_intr_ops: MIC HW specific interrupt operations
+ * @intr_init: Initialize H/W specific interrupt information.
+ * @enable_interrupts: Enable interrupts from the hardware.
+ * @disable_interrupts: Disable interrupts from the hardware.
+ * @program_msi_to_src_map: Update MSI mapping registers with
+ * irq information.
+ * @read_msi_to_src_map: Read MSI mapping registers containing
+ * irq information.
+ */
+struct mic_hw_intr_ops {
+       void (*intr_init)(struct mic_device *mdev);
+       void (*enable_interrupts)(struct mic_device *mdev);
+       void (*disable_interrupts)(struct mic_device *mdev);
+       void (*program_msi_to_src_map) (struct mic_device *mdev,
+                       int idx, int intr_src, bool set);
+       u32 (*read_msi_to_src_map) (struct mic_device *mdev,
+                       int idx);
+};
+
+int mic_next_db(struct mic_device *mdev);
+struct mic_irq *mic_request_irq(struct mic_device *mdev,
+       irqreturn_t (*func)(int irq, void *data),
+       const char *name, void *data, int intr_src,
+       enum mic_intr_type type);
+
+void mic_free_irq(struct mic_device *mdev,
+               struct mic_irq *cookie, void *data);
+int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev);
+void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev);
+void mic_intr_restore(struct mic_device *mdev);
+#endif
diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c
new file mode 100644 (file)
index 0000000..ad838c7
--- /dev/null
@@ -0,0 +1,536 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ * Global TODO's across the driver to be added after initial base
+ * patches are accepted upstream:
+ * 1) Enable DMA support.
+ * 2) Enable per vring interrupt support.
+ */
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/suspend.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_x100.h"
+#include "mic_smpt.h"
+#include "mic_fops.h"
+#include "mic_virtio.h"
+
+static const char mic_driver_name[] = "mic";
+
+static DEFINE_PCI_DEVICE_TABLE(mic_pci_tbl) = {
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2253)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2254)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2255)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2256)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2257)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2258)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2259)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225a)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225b)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225c)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225d)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225e)},
+
+       /* required last entry */
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, mic_pci_tbl);
+
+/* ID allocator for MIC devices */
+static struct ida g_mic_ida;
+/* Class of MIC devices for sysfs accessibility. */
+static struct class *g_mic_class;
+/* Base device node number for MIC devices */
+static dev_t g_mic_devno;
+
+static const struct file_operations mic_fops = {
+       .open = mic_open,
+       .release = mic_release,
+       .unlocked_ioctl = mic_ioctl,
+       .poll = mic_poll,
+       .mmap = mic_mmap,
+       .owner = THIS_MODULE,
+};
+
+/* Initialize the device page */
+static int mic_dp_init(struct mic_device *mdev)
+{
+       mdev->dp = kzalloc(MIC_DP_SIZE, GFP_KERNEL);
+       if (!mdev->dp) {
+               dev_err(mdev->sdev->parent, "%s %d err %d\n",
+                       __func__, __LINE__, -ENOMEM);
+               return -ENOMEM;
+       }
+
+       mdev->dp_dma_addr = mic_map_single(mdev,
+               mdev->dp, MIC_DP_SIZE);
+       if (mic_map_error(mdev->dp_dma_addr)) {
+               kfree(mdev->dp);
+               dev_err(mdev->sdev->parent, "%s %d err %d\n",
+                       __func__, __LINE__, -ENOMEM);
+               return -ENOMEM;
+       }
+       mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr);
+       mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32);
+       return 0;
+}
+
+/* Uninitialize the device page */
+static void mic_dp_uninit(struct mic_device *mdev)
+{
+       mic_unmap_single(mdev, mdev->dp_dma_addr, MIC_DP_SIZE);
+       kfree(mdev->dp);
+}
+
+/**
+ * mic_shutdown_db - Shutdown doorbell interrupt handler.
+ */
+static irqreturn_t mic_shutdown_db(int irq, void *data)
+{
+       struct mic_device *mdev = data;
+       struct mic_bootparam *bootparam = mdev->dp;
+
+       mdev->ops->ack_interrupt(mdev);
+
+       switch (bootparam->shutdown_status) {
+       case MIC_HALTED:
+       case MIC_POWER_OFF:
+       case MIC_RESTART:
+               /* Fall through */
+       case MIC_CRASHED:
+               schedule_work(&mdev->shutdown_work);
+               break;
+       default:
+               break;
+       };
+       return IRQ_HANDLED;
+}
+
+/**
+ * mic_ops_init: Initialize HW specific operation tables.
+ *
+ * @mdev: pointer to mic_device instance
+ *
+ * returns none.
+ */
+static void mic_ops_init(struct mic_device *mdev)
+{
+       switch (mdev->family) {
+       case MIC_FAMILY_X100:
+               mdev->ops = &mic_x100_ops;
+               mdev->intr_ops = &mic_x100_intr_ops;
+               mdev->smpt_ops = &mic_x100_smpt_ops;
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ * mic_get_family - Determine hardware family to which this MIC belongs.
+ *
+ * @pdev: The pci device structure
+ *
+ * returns family.
+ */
+static enum mic_hw_family mic_get_family(struct pci_dev *pdev)
+{
+       enum mic_hw_family family;
+
+       switch (pdev->device) {
+       case MIC_X100_PCI_DEVICE_2250:
+       case MIC_X100_PCI_DEVICE_2251:
+       case MIC_X100_PCI_DEVICE_2252:
+       case MIC_X100_PCI_DEVICE_2253:
+       case MIC_X100_PCI_DEVICE_2254:
+       case MIC_X100_PCI_DEVICE_2255:
+       case MIC_X100_PCI_DEVICE_2256:
+       case MIC_X100_PCI_DEVICE_2257:
+       case MIC_X100_PCI_DEVICE_2258:
+       case MIC_X100_PCI_DEVICE_2259:
+       case MIC_X100_PCI_DEVICE_225a:
+       case MIC_X100_PCI_DEVICE_225b:
+       case MIC_X100_PCI_DEVICE_225c:
+       case MIC_X100_PCI_DEVICE_225d:
+       case MIC_X100_PCI_DEVICE_225e:
+               family = MIC_FAMILY_X100;
+               break;
+       default:
+               family = MIC_FAMILY_UNKNOWN;
+               break;
+       }
+       return family;
+}
+
+/**
+* mic_pm_notifier: Notifier callback function that handles
+* PM notifications.
+*
+* @notifier_block: The notifier structure.
+* @pm_event: The event for which the driver was notified.
+* @unused: Meaningless. Always NULL.
+*
+* returns NOTIFY_DONE
+*/
+static int mic_pm_notifier(struct notifier_block *notifier,
+               unsigned long pm_event, void *unused)
+{
+       struct mic_device *mdev = container_of(notifier,
+               struct mic_device, pm_notifier);
+
+       switch (pm_event) {
+       case PM_HIBERNATION_PREPARE:
+               /* Fall through */
+       case PM_SUSPEND_PREPARE:
+               mic_prepare_suspend(mdev);
+               break;
+       case PM_POST_HIBERNATION:
+               /* Fall through */
+       case PM_POST_SUSPEND:
+               /* Fall through */
+       case PM_POST_RESTORE:
+               mic_complete_resume(mdev);
+               break;
+       case PM_RESTORE_PREPARE:
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+/**
+ * mic_device_init - Allocates and initializes the MIC device structure
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: The pci device structure
+ *
+ * returns none.
+ */
+static int
+mic_device_init(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc;
+
+       mdev->family = mic_get_family(pdev);
+       mdev->stepping = pdev->revision;
+       mic_ops_init(mdev);
+       mic_sysfs_init(mdev);
+       mutex_init(&mdev->mic_mutex);
+       mdev->irq_info.next_avail_src = 0;
+       INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work);
+       INIT_WORK(&mdev->shutdown_work, mic_shutdown_work);
+       init_completion(&mdev->reset_wait);
+       INIT_LIST_HEAD(&mdev->vdev_list);
+       mdev->pm_notifier.notifier_call = mic_pm_notifier;
+       rc = register_pm_notifier(&mdev->pm_notifier);
+       if (rc) {
+               dev_err(&pdev->dev, "register_pm_notifier failed rc %d\n",
+                       rc);
+               goto register_pm_notifier_fail;
+       }
+       return 0;
+register_pm_notifier_fail:
+       flush_work(&mdev->shutdown_work);
+       flush_work(&mdev->reset_trigger_work);
+       return rc;
+}
+
+/**
+ * mic_device_uninit - Frees resources allocated during mic_device_init(..)
+ *
+ * @mdev: pointer to mic_device instance
+ *
+ * returns none
+ */
+static void mic_device_uninit(struct mic_device *mdev)
+{
+       /* The cmdline sysfs entry might have allocated cmdline */
+       kfree(mdev->cmdline);
+       kfree(mdev->firmware);
+       kfree(mdev->ramdisk);
+       kfree(mdev->bootmode);
+       flush_work(&mdev->reset_trigger_work);
+       flush_work(&mdev->shutdown_work);
+       unregister_pm_notifier(&mdev->pm_notifier);
+}
+
+/**
+ * mic_probe - Device Initialization Routine
+ *
+ * @pdev: PCI device structure
+ * @ent: entry in mic_pci_tbl
+ *
+ * returns 0 on success, < 0 on failure.
+ */
+static int mic_probe(struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+       int rc;
+       struct mic_device *mdev;
+
+       mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+       if (!mdev) {
+               rc = -ENOMEM;
+               dev_err(&pdev->dev, "mdev kmalloc failed rc %d\n", rc);
+               goto mdev_alloc_fail;
+       }
+       mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL);
+       if (mdev->id < 0) {
+               rc = mdev->id;
+               dev_err(&pdev->dev, "ida_simple_get failed rc %d\n", rc);
+               goto ida_fail;
+       }
+
+       rc = mic_device_init(mdev, pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "mic_device_init failed rc %d\n", rc);
+               goto device_init_fail;
+       }
+
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to enable pci device.\n");
+               goto uninit_device;
+       }
+
+       pci_set_master(pdev);
+
+       rc = pci_request_regions(pdev, mic_driver_name);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to get pci regions.\n");
+               goto disable_device;
+       }
+
+       rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+       if (rc) {
+               dev_err(&pdev->dev, "Cannot set DMA mask\n");
+               goto release_regions;
+       }
+
+       mdev->mmio.pa = pci_resource_start(pdev, mdev->ops->mmio_bar);
+       mdev->mmio.len = pci_resource_len(pdev, mdev->ops->mmio_bar);
+       mdev->mmio.va = pci_ioremap_bar(pdev, mdev->ops->mmio_bar);
+       if (!mdev->mmio.va) {
+               dev_err(&pdev->dev, "Cannot remap MMIO BAR\n");
+               rc = -EIO;
+               goto release_regions;
+       }
+
+       mdev->aper.pa = pci_resource_start(pdev, mdev->ops->aper_bar);
+       mdev->aper.len = pci_resource_len(pdev, mdev->ops->aper_bar);
+       mdev->aper.va = ioremap_wc(mdev->aper.pa, mdev->aper.len);
+       if (!mdev->aper.va) {
+               dev_err(&pdev->dev, "Cannot remap Aperture BAR\n");
+               rc = -EIO;
+               goto unmap_mmio;
+       }
+
+       mdev->intr_ops->intr_init(mdev);
+       rc = mic_setup_interrupts(mdev, pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "mic_setup_interrupts failed %d\n", rc);
+               goto unmap_aper;
+       }
+       rc = mic_smpt_init(mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "smpt_init failed %d\n", rc);
+               goto free_interrupts;
+       }
+
+       pci_set_drvdata(pdev, mdev);
+
+       mdev->sdev = device_create_with_groups(g_mic_class, &pdev->dev,
+               MKDEV(MAJOR(g_mic_devno), mdev->id), NULL,
+               mdev->attr_group, "mic%d", mdev->id);
+       if (IS_ERR(mdev->sdev)) {
+               rc = PTR_ERR(mdev->sdev);
+               dev_err(&pdev->dev,
+                       "device_create_with_groups failed rc %d\n", rc);
+               goto smpt_uninit;
+       }
+       mdev->state_sysfs = sysfs_get_dirent(mdev->sdev->kobj.sd, "state");
+       if (!mdev->state_sysfs) {
+               rc = -ENODEV;
+               dev_err(&pdev->dev, "sysfs_get_dirent failed rc %d\n", rc);
+               goto destroy_device;
+       }
+
+       rc = mic_dp_init(mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "mic_dp_init failed rc %d\n", rc);
+               goto sysfs_put;
+       }
+       mutex_lock(&mdev->mic_mutex);
+
+       mdev->shutdown_db = mic_next_db(mdev);
+       mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db,
+               "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB);
+       if (IS_ERR(mdev->shutdown_cookie)) {
+               rc = PTR_ERR(mdev->shutdown_cookie);
+               mutex_unlock(&mdev->mic_mutex);
+               goto dp_uninit;
+       }
+       mutex_unlock(&mdev->mic_mutex);
+       mic_bootparam_init(mdev);
+
+       mic_create_debug_dir(mdev);
+       cdev_init(&mdev->cdev, &mic_fops);
+       mdev->cdev.owner = THIS_MODULE;
+       rc = cdev_add(&mdev->cdev, MKDEV(MAJOR(g_mic_devno), mdev->id), 1);
+       if (rc) {
+               dev_err(&pdev->dev, "cdev_add err id %d rc %d\n", mdev->id, rc);
+               goto cleanup_debug_dir;
+       }
+       return 0;
+cleanup_debug_dir:
+       mic_delete_debug_dir(mdev);
+       mutex_lock(&mdev->mic_mutex);
+       mic_free_irq(mdev, mdev->shutdown_cookie, mdev);
+       mutex_unlock(&mdev->mic_mutex);
+dp_uninit:
+       mic_dp_uninit(mdev);
+sysfs_put:
+       sysfs_put(mdev->state_sysfs);
+destroy_device:
+       device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id));
+smpt_uninit:
+       mic_smpt_uninit(mdev);
+free_interrupts:
+       mic_free_interrupts(mdev, pdev);
+unmap_aper:
+       iounmap(mdev->aper.va);
+unmap_mmio:
+       iounmap(mdev->mmio.va);
+release_regions:
+       pci_release_regions(pdev);
+disable_device:
+       pci_disable_device(pdev);
+uninit_device:
+       mic_device_uninit(mdev);
+device_init_fail:
+       ida_simple_remove(&g_mic_ida, mdev->id);
+ida_fail:
+       kfree(mdev);
+mdev_alloc_fail:
+       dev_err(&pdev->dev, "Probe failed rc %d\n", rc);
+       return rc;
+}
+
+/**
+ * mic_remove - Device Removal Routine
+ * mic_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device.
+ *
+ * @pdev: PCI device structure
+ */
+static void mic_remove(struct pci_dev *pdev)
+{
+       struct mic_device *mdev;
+
+       mdev = pci_get_drvdata(pdev);
+       if (!mdev)
+               return;
+
+       mic_stop(mdev, false);
+       cdev_del(&mdev->cdev);
+       mic_delete_debug_dir(mdev);
+       mutex_lock(&mdev->mic_mutex);
+       mic_free_irq(mdev, mdev->shutdown_cookie, mdev);
+       mutex_unlock(&mdev->mic_mutex);
+       flush_work(&mdev->shutdown_work);
+       mic_dp_uninit(mdev);
+       sysfs_put(mdev->state_sysfs);
+       device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id));
+       mic_smpt_uninit(mdev);
+       mic_free_interrupts(mdev, pdev);
+       iounmap(mdev->mmio.va);
+       iounmap(mdev->aper.va);
+       mic_device_uninit(mdev);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       ida_simple_remove(&g_mic_ida, mdev->id);
+       kfree(mdev);
+}
+static struct pci_driver mic_driver = {
+       .name = mic_driver_name,
+       .id_table = mic_pci_tbl,
+       .probe = mic_probe,
+       .remove = mic_remove
+};
+
+static int __init mic_init(void)
+{
+       int ret;
+
+       ret = alloc_chrdev_region(&g_mic_devno, 0,
+               MIC_MAX_NUM_DEVS, mic_driver_name);
+       if (ret) {
+               pr_err("alloc_chrdev_region failed ret %d\n", ret);
+               goto error;
+       }
+
+       g_mic_class = class_create(THIS_MODULE, mic_driver_name);
+       if (IS_ERR(g_mic_class)) {
+               ret = PTR_ERR(g_mic_class);
+               pr_err("class_create failed ret %d\n", ret);
+               goto cleanup_chrdev;
+       }
+
+       mic_init_debugfs();
+       ida_init(&g_mic_ida);
+       ret = pci_register_driver(&mic_driver);
+       if (ret) {
+               pr_err("pci_register_driver failed ret %d\n", ret);
+               goto cleanup_debugfs;
+       }
+       return ret;
+cleanup_debugfs:
+       mic_exit_debugfs();
+       class_destroy(g_mic_class);
+cleanup_chrdev:
+       unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS);
+error:
+       return ret;
+}
+
+static void __exit mic_exit(void)
+{
+       pci_unregister_driver(&mic_driver);
+       ida_destroy(&g_mic_ida);
+       mic_exit_debugfs();
+       class_destroy(g_mic_class);
+       unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS);
+}
+
+module_init(mic_init);
+module_exit(mic_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel(R) MIC X100 Host driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c
new file mode 100644 (file)
index 0000000..fae474c
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/pci.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_smpt.h"
+
+static inline u64 mic_system_page_mask(struct mic_device *mdev)
+{
+       return (1ULL << mdev->smpt->info.page_shift) - 1ULL;
+}
+
+static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa)
+{
+       return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift;
+}
+
+static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index)
+{
+       return mdev->smpt->info.base + (index * mdev->smpt->info.page_size);
+}
+
+static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa)
+{
+       return pa & mic_system_page_mask(mdev);
+}
+
+static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa)
+{
+       return ALIGN(pa - mic_system_page_mask(mdev),
+               mdev->smpt->info.page_size);
+}
+
+static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa)
+{
+       return ALIGN(pa, mdev->smpt->info.page_size);
+}
+
+/* Total Cumulative system memory accessible by MIC across all SMPT entries */
+static inline u64 mic_max_system_memory(struct mic_device *mdev)
+{
+       return mdev->smpt->info.num_reg * mdev->smpt->info.page_size;
+}
+
+/* Maximum system memory address accessible by MIC */
+static inline u64 mic_max_system_addr(struct mic_device *mdev)
+{
+       return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL;
+}
+
+/* Check if the DMA address is a MIC system memory address */
+static inline bool
+mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa)
+{
+       return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev);
+}
+
+/* Populate an SMPT entry and update the reference counts. */
+static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr,
+               int entries, struct mic_device *mdev)
+{
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+       int i;
+
+       for (i = spt; i < spt + entries; i++,
+               addr += smpt_info->info.page_size) {
+               if (!smpt_info->entry[i].ref_count &&
+                   (smpt_info->entry[i].dma_addr != addr)) {
+                       mdev->smpt_ops->set(mdev, addr, i);
+                       smpt_info->entry[i].dma_addr = addr;
+               }
+               smpt_info->entry[i].ref_count += ref[i - spt];
+       }
+}
+
+/*
+ * Find an available MIC address in MIC SMPT address space
+ * for a given DMA address and size.
+ */
+static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr,
+                               int entries, s64 *ref, size_t size)
+{
+       int spt;
+       int ae = 0;
+       int i;
+       unsigned long flags;
+       dma_addr_t mic_addr = 0;
+       dma_addr_t addr = dma_addr;
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+
+       spin_lock_irqsave(&smpt_info->smpt_lock, flags);
+
+       /* find existing entries */
+       for (i = 0; i < smpt_info->info.num_reg; i++) {
+               if (smpt_info->entry[i].dma_addr == addr) {
+                       ae++;
+                       addr += smpt_info->info.page_size;
+               } else if (ae) /* cannot find contiguous entries */
+                       goto not_found;
+
+               if (ae == entries)
+                       goto found;
+       }
+
+       /* find free entry */
+       for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) {
+               ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0;
+               if (ae == entries)
+                       goto found;
+       }
+
+not_found:
+       spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
+       return mic_addr;
+
+found:
+       spt = i - entries + 1;
+       mic_addr = mic_smpt_to_pa(mdev, spt);
+       mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev);
+       smpt_info->map_count++;
+       smpt_info->ref_count += (s64)size;
+       spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
+       return mic_addr;
+}
+
+/*
+ * Returns number of smpt entries needed for dma_addr to dma_addr + size
+ * also returns the reference count array for each of those entries
+ * and the starting smpt address
+ */
+static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr,
+                               size_t size, s64 *ref,  u64 *smpt_start)
+{
+       u64 start =  dma_addr;
+       u64 end = dma_addr + size;
+       int i = 0;
+
+       while (start < end) {
+               ref[i++] = min(mic_smpt_align_high(mdev, start + 1),
+                       end) - start;
+               start = mic_smpt_align_high(mdev, start + 1);
+       }
+
+       if (smpt_start)
+               *smpt_start = mic_smpt_align_low(mdev, dma_addr);
+
+       return i;
+}
+
+/*
+ * mic_to_dma_addr - Converts a MIC address to a DMA address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @mic_addr: MIC address.
+ *
+ * returns a DMA address.
+ */
+static dma_addr_t
+mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr)
+{
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+       int spt;
+       dma_addr_t dma_addr;
+
+       if (!mic_is_system_addr(mdev, mic_addr)) {
+               dev_err(mdev->sdev->parent,
+                       "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr);
+               return -EINVAL;
+       }
+       spt = mic_sys_addr_to_smpt(mdev, mic_addr);
+       dma_addr = smpt_info->entry[spt].dma_addr +
+               mic_smpt_offset(mdev, mic_addr);
+       return dma_addr;
+}
+
+/**
+ * mic_map - Maps a DMA address to a MIC physical address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @dma_addr: DMA address.
+ * @size: Size of the region to be mapped.
+ *
+ * This API converts the DMA address provided to a DMA address understood
+ * by MIC. Caller should check for errors by calling mic_map_error(..).
+ *
+ * returns DMA address as required by MIC.
+ */
+dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size)
+{
+       dma_addr_t mic_addr = 0;
+       int num_entries;
+       s64 *ref;
+       u64 smpt_start;
+
+       if (!size || size > mic_max_system_memory(mdev))
+               return mic_addr;
+
+       ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL);
+       if (!ref)
+               return mic_addr;
+
+       num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size,
+               ref, &smpt_start);
+
+       /* Set the smpt table appropriately and get 16G aligned mic address */
+       mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size);
+
+       kfree(ref);
+
+       /*
+        * If mic_addr is zero then its an error case
+        * since mic_addr can never be zero.
+        * else generate mic_addr by adding the 16G offset in dma_addr
+        */
+       if (!mic_addr && MIC_FAMILY_X100 == mdev->family) {
+               dev_err(mdev->sdev->parent,
+                       "mic_map failed dma_addr 0x%llx size 0x%lx\n",
+                       dma_addr, size);
+               return mic_addr;
+       } else {
+               return mic_addr + mic_smpt_offset(mdev, dma_addr);
+       }
+}
+
+/**
+ * mic_unmap - Unmaps a MIC physical address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @mic_addr: MIC physical address.
+ * @size: Size of the region to be unmapped.
+ *
+ * This API unmaps the mappings created by mic_map(..).
+ *
+ * returns None.
+ */
+void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size)
+{
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+       s64 *ref;
+       int num_smpt;
+       int spt;
+       int i;
+       unsigned long flags;
+
+       if (!size)
+               return;
+
+       if (!mic_is_system_addr(mdev, mic_addr)) {
+               dev_err(mdev->sdev->parent,
+                       "invalid address: 0x%llx\n", mic_addr);
+               return;
+       }
+
+       spt = mic_sys_addr_to_smpt(mdev, mic_addr);
+       ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL);
+       if (!ref)
+               return;
+
+       /* Get number of smpt entries to be mapped, ref count array */
+       num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL);
+
+       spin_lock_irqsave(&smpt_info->smpt_lock, flags);
+       smpt_info->unmap_count++;
+       smpt_info->ref_count -= (s64)size;
+
+       for (i = spt; i < spt + num_smpt; i++) {
+               smpt_info->entry[i].ref_count -= ref[i - spt];
+               if (smpt_info->entry[i].ref_count < 0)
+                       dev_warn(mdev->sdev->parent,
+                                "ref count for entry %d is negative\n", i);
+       }
+       spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
+       kfree(ref);
+}
+
+/**
+ * mic_map_single - Maps a virtual address to a MIC physical address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @va: Kernel direct mapped virtual address.
+ * @size: Size of the region to be mapped.
+ *
+ * This API calls pci_map_single(..) for the direct mapped virtual address
+ * and then converts the DMA address provided to a DMA address understood
+ * by MIC. Caller should check for errors by calling mic_map_error(..).
+ *
+ * returns DMA address as required by MIC.
+ */
+dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size)
+{
+       dma_addr_t mic_addr = 0;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+       dma_addr_t dma_addr =
+               pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL);
+
+       if (!pci_dma_mapping_error(pdev, dma_addr)) {
+               mic_addr = mic_map(mdev, dma_addr, size);
+               if (!mic_addr) {
+                       dev_err(mdev->sdev->parent,
+                               "mic_map failed dma_addr 0x%llx size 0x%lx\n",
+                               dma_addr, size);
+                       pci_unmap_single(pdev, dma_addr,
+                                        size, PCI_DMA_BIDIRECTIONAL);
+               }
+       }
+       return mic_addr;
+}
+
+/**
+ * mic_unmap_single - Unmaps a MIC physical address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @mic_addr: MIC physical address.
+ * @size: Size of the region to be unmapped.
+ *
+ * This API unmaps the mappings created by mic_map_single(..).
+ *
+ * returns None.
+ */
+void
+mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size)
+{
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+       dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr);
+       mic_unmap(mdev, mic_addr, size);
+       pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL);
+}
+
+/**
+ * mic_smpt_init - Initialize MIC System Memory Page Tables.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * returns 0 for success and -errno for error.
+ */
+int mic_smpt_init(struct mic_device *mdev)
+{
+       int i, err = 0;
+       dma_addr_t dma_addr;
+       struct mic_smpt_info *smpt_info;
+
+       mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL);
+       if (!mdev->smpt)
+               return -ENOMEM;
+
+       smpt_info = mdev->smpt;
+       mdev->smpt_ops->init(mdev);
+       smpt_info->entry = kmalloc_array(smpt_info->info.num_reg,
+                                        sizeof(*smpt_info->entry), GFP_KERNEL);
+       if (!smpt_info->entry) {
+               err = -ENOMEM;
+               goto free_smpt;
+       }
+       spin_lock_init(&smpt_info->smpt_lock);
+       for (i = 0; i < smpt_info->info.num_reg; i++) {
+               dma_addr = i * smpt_info->info.page_size;
+               smpt_info->entry[i].dma_addr = dma_addr;
+               smpt_info->entry[i].ref_count = 0;
+               mdev->smpt_ops->set(mdev, dma_addr, i);
+       }
+       smpt_info->ref_count = 0;
+       smpt_info->map_count = 0;
+       smpt_info->unmap_count = 0;
+       return 0;
+free_smpt:
+       kfree(smpt_info);
+       return err;
+}
+
+/**
+ * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * returns None.
+ */
+void mic_smpt_uninit(struct mic_device *mdev)
+{
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+       int i;
+
+       dev_dbg(mdev->sdev->parent,
+               "nodeid %d SMPT ref count %lld map %lld unmap %lld\n",
+               mdev->id, smpt_info->ref_count,
+               smpt_info->map_count, smpt_info->unmap_count);
+
+       for (i = 0; i < smpt_info->info.num_reg; i++) {
+               dev_dbg(mdev->sdev->parent,
+                       "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n",
+                       i, smpt_info->entry[i].dma_addr,
+                       smpt_info->entry[i].ref_count);
+               if (smpt_info->entry[i].ref_count)
+                       dev_warn(mdev->sdev->parent,
+                                "ref count for entry %d is not zero\n", i);
+       }
+       kfree(smpt_info->entry);
+       kfree(smpt_info);
+}
+
+/**
+ * mic_smpt_restore - Restore MIC System Memory Page Tables.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * Restore the SMPT registers to values previously stored in the
+ * SW data structures. Some MIC steppings lose register state
+ * across resets and this API should be called for performing
+ * a restore operation if required.
+ *
+ * returns None.
+ */
+void mic_smpt_restore(struct mic_device *mdev)
+{
+       int i;
+       dma_addr_t dma_addr;
+
+       for (i = 0; i < mdev->smpt->info.num_reg; i++) {
+               dma_addr = mdev->smpt->entry[i].dma_addr;
+               mdev->smpt_ops->set(mdev, dma_addr, i);
+       }
+}
diff --git a/drivers/misc/mic/host/mic_smpt.h b/drivers/misc/mic/host/mic_smpt.h
new file mode 100644 (file)
index 0000000..51970ab
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef MIC_SMPT_H
+#define MIC_SMPT_H
+/**
+ * struct mic_smpt_ops - MIC HW specific SMPT operations.
+ * @init: Initialize hardware specific SMPT information in mic_smpt_hw_info.
+ * @set: Set the value for a particular SMPT entry.
+ */
+struct mic_smpt_ops {
+       void (*init)(struct mic_device *mdev);
+       void (*set)(struct mic_device *mdev, dma_addr_t dma_addr, u8 index);
+};
+
+/**
+ * struct mic_smpt - MIC SMPT entry information.
+ * @dma_addr: Base DMA address for this SMPT entry.
+ * @ref_count: Number of active mappings for this SMPT entry in bytes.
+ */
+struct mic_smpt {
+       dma_addr_t dma_addr;
+       s64 ref_count;
+};
+
+/**
+ * struct mic_smpt_hw_info - MIC SMPT hardware specific information.
+ * @num_reg: Number of SMPT registers.
+ * @page_shift: System memory page shift.
+ * @page_size: System memory page size.
+ * @base: System address base.
+ */
+struct mic_smpt_hw_info {
+       u8 num_reg;
+       u8 page_shift;
+       u64 page_size;
+       u64 base;
+};
+
+/**
+ * struct mic_smpt_info - MIC SMPT information.
+ * @entry: Array of SMPT entries.
+ * @smpt_lock: Spin lock protecting access to SMPT data structures.
+ * @info: Hardware specific SMPT information.
+ * @ref_count: Number of active SMPT mappings (for debug).
+ * @map_count: Number of SMPT mappings created (for debug).
+ * @unmap_count: Number of SMPT mappings destroyed (for debug).
+ */
+struct mic_smpt_info {
+       struct mic_smpt *entry;
+       spinlock_t smpt_lock;
+       struct mic_smpt_hw_info info;
+       s64 ref_count;
+       s64 map_count;
+       s64 unmap_count;
+};
+
+dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size);
+void mic_unmap_single(struct mic_device *mdev,
+       dma_addr_t mic_addr, size_t size);
+dma_addr_t mic_map(struct mic_device *mdev,
+       dma_addr_t dma_addr, size_t size);
+void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size);
+
+/**
+ * mic_map_error - Check a MIC address for errors.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * returns Whether there was an error during mic_map..(..) APIs.
+ */
+static inline bool mic_map_error(dma_addr_t mic_addr)
+{
+       return !mic_addr;
+}
+
+int mic_smpt_init(struct mic_device *mdev);
+void mic_smpt_uninit(struct mic_device *mdev);
+void mic_smpt_restore(struct mic_device *mdev);
+
+#endif
diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c
new file mode 100644 (file)
index 0000000..6dd864e
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/pci.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+
+/*
+ * A state-to-string lookup table, for exposing a human readable state
+ * via sysfs. Always keep in sync with enum mic_states
+ */
+static const char * const mic_state_string[] = {
+       [MIC_OFFLINE] = "offline",
+       [MIC_ONLINE] = "online",
+       [MIC_SHUTTING_DOWN] = "shutting_down",
+       [MIC_RESET_FAILED] = "reset_failed",
+       [MIC_SUSPENDING] = "suspending",
+       [MIC_SUSPENDED] = "suspended",
+};
+
+/*
+ * A shutdown-status-to-string lookup table, for exposing a human
+ * readable state via sysfs. Always keep in sync with enum mic_shutdown_status
+ */
+static const char * const mic_shutdown_status_string[] = {
+       [MIC_NOP] = "nop",
+       [MIC_CRASHED] = "crashed",
+       [MIC_HALTED] = "halted",
+       [MIC_POWER_OFF] = "poweroff",
+       [MIC_RESTART] = "restart",
+};
+
+void mic_set_shutdown_status(struct mic_device *mdev, u8 shutdown_status)
+{
+       dev_dbg(mdev->sdev->parent, "Shutdown Status %s -> %s\n",
+               mic_shutdown_status_string[mdev->shutdown_status],
+               mic_shutdown_status_string[shutdown_status]);
+       mdev->shutdown_status = shutdown_status;
+}
+
+void mic_set_state(struct mic_device *mdev, u8 state)
+{
+       dev_dbg(mdev->sdev->parent, "State %s -> %s\n",
+               mic_state_string[mdev->state],
+               mic_state_string[state]);
+       mdev->state = state;
+       sysfs_notify_dirent(mdev->state_sysfs);
+}
+
+static ssize_t
+family_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       static const char x100[] = "x100";
+       static const char unknown[] = "Unknown";
+       const char *card = NULL;
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       switch (mdev->family) {
+       case MIC_FAMILY_X100:
+               card = x100;
+               break;
+       default:
+               card = unknown;
+               break;
+       }
+       return scnprintf(buf, PAGE_SIZE, "%s\n", card);
+}
+static DEVICE_ATTR_RO(family);
+
+static ssize_t
+stepping_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *string = "??";
+
+       if (!mdev)
+               return -EINVAL;
+
+       switch (mdev->stepping) {
+       case MIC_A0_STEP:
+               string = "A0";
+               break;
+       case MIC_B0_STEP:
+               string = "B0";
+               break;
+       case MIC_B1_STEP:
+               string = "B1";
+               break;
+       case MIC_C0_STEP:
+               string = "C0";
+               break;
+       default:
+               break;
+       }
+       return scnprintf(buf, PAGE_SIZE, "%s\n", string);
+}
+static DEVICE_ATTR_RO(stepping);
+
+static ssize_t
+state_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev || mdev->state >= MIC_LAST)
+               return -EINVAL;
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n",
+               mic_state_string[mdev->state]);
+}
+
+static ssize_t
+state_store(struct device *dev, struct device_attribute *attr,
+           const char *buf, size_t count)
+{
+       int rc = 0;
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       if (!mdev)
+               return -EINVAL;
+       if (sysfs_streq(buf, "boot")) {
+               rc = mic_start(mdev, buf);
+               if (rc) {
+                       dev_err(mdev->sdev->parent,
+                               "mic_boot failed rc %d\n", rc);
+                       count = rc;
+               }
+               goto done;
+       }
+
+       if (sysfs_streq(buf, "reset")) {
+               schedule_work(&mdev->reset_trigger_work);
+               goto done;
+       }
+
+       if (sysfs_streq(buf, "shutdown")) {
+               mic_shutdown(mdev);
+               goto done;
+       }
+
+       if (sysfs_streq(buf, "suspend")) {
+               mic_suspend(mdev);
+               goto done;
+       }
+
+       count = -EINVAL;
+done:
+       return count;
+}
+static DEVICE_ATTR_RW(state);
+
+static ssize_t shutdown_status_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev || mdev->shutdown_status >= MIC_STATUS_LAST)
+               return -EINVAL;
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n",
+               mic_shutdown_status_string[mdev->shutdown_status]);
+}
+static DEVICE_ATTR_RO(shutdown_status);
+
+static ssize_t
+cmdline_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *cmdline;
+
+       if (!mdev)
+               return -EINVAL;
+
+       cmdline = mdev->cmdline;
+
+       if (cmdline)
+               return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline);
+       return 0;
+}
+
+static ssize_t
+cmdline_store(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       mutex_lock(&mdev->mic_mutex);
+       kfree(mdev->cmdline);
+
+       mdev->cmdline = kmalloc(count + 1, GFP_KERNEL);
+       if (!mdev->cmdline) {
+               count = -ENOMEM;
+               goto unlock;
+       }
+
+       strncpy(mdev->cmdline, buf, count);
+
+       if (mdev->cmdline[count - 1] == '\n')
+               mdev->cmdline[count - 1] = '\0';
+       else
+               mdev->cmdline[count] = '\0';
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+       return count;
+}
+static DEVICE_ATTR_RW(cmdline);
+
+static ssize_t
+firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *firmware;
+
+       if (!mdev)
+               return -EINVAL;
+
+       firmware = mdev->firmware;
+
+       if (firmware)
+               return scnprintf(buf, PAGE_SIZE, "%s\n", firmware);
+       return 0;
+}
+
+static ssize_t
+firmware_store(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       mutex_lock(&mdev->mic_mutex);
+       kfree(mdev->firmware);
+
+       mdev->firmware = kmalloc(count + 1, GFP_KERNEL);
+       if (!mdev->firmware) {
+               count = -ENOMEM;
+               goto unlock;
+       }
+       strncpy(mdev->firmware, buf, count);
+
+       if (mdev->firmware[count - 1] == '\n')
+               mdev->firmware[count - 1] = '\0';
+       else
+               mdev->firmware[count] = '\0';
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+       return count;
+}
+static DEVICE_ATTR_RW(firmware);
+
+static ssize_t
+ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *ramdisk;
+
+       if (!mdev)
+               return -EINVAL;
+
+       ramdisk = mdev->ramdisk;
+
+       if (ramdisk)
+               return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk);
+       return 0;
+}
+
+static ssize_t
+ramdisk_store(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       mutex_lock(&mdev->mic_mutex);
+       kfree(mdev->ramdisk);
+
+       mdev->ramdisk = kmalloc(count + 1, GFP_KERNEL);
+       if (!mdev->ramdisk) {
+               count = -ENOMEM;
+               goto unlock;
+       }
+
+       strncpy(mdev->ramdisk, buf, count);
+
+       if (mdev->ramdisk[count - 1] == '\n')
+               mdev->ramdisk[count - 1] = '\0';
+       else
+               mdev->ramdisk[count] = '\0';
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+       return count;
+}
+static DEVICE_ATTR_RW(ramdisk);
+
+static ssize_t
+bootmode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *bootmode;
+
+       if (!mdev)
+               return -EINVAL;
+
+       bootmode = mdev->bootmode;
+
+       if (bootmode)
+               return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode);
+       return 0;
+}
+
+static ssize_t
+bootmode_store(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "elf"))
+               return -EINVAL;
+
+       mutex_lock(&mdev->mic_mutex);
+       kfree(mdev->bootmode);
+
+       mdev->bootmode = kmalloc(count + 1, GFP_KERNEL);
+       if (!mdev->bootmode) {
+               count = -ENOMEM;
+               goto unlock;
+       }
+
+       strncpy(mdev->bootmode, buf, count);
+
+       if (mdev->bootmode[count - 1] == '\n')
+               mdev->bootmode[count - 1] = '\0';
+       else
+               mdev->bootmode[count] = '\0';
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+       return count;
+}
+static DEVICE_ATTR_RW(bootmode);
+
+static ssize_t
+log_buf_addr_show(struct device *dev, struct device_attribute *attr,
+                 char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_addr);
+}
+
+static ssize_t
+log_buf_addr_store(struct device *dev, struct device_attribute *attr,
+                  const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       int ret;
+       unsigned long addr;
+
+       if (!mdev)
+               return -EINVAL;
+
+       ret = kstrtoul(buf, 16, &addr);
+       if (ret)
+               goto exit;
+
+       mdev->log_buf_addr = (void *)addr;
+       ret = count;
+exit:
+       return ret;
+}
+static DEVICE_ATTR_RW(log_buf_addr);
+
+static ssize_t
+log_buf_len_show(struct device *dev, struct device_attribute *attr,
+                char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_len);
+}
+
+static ssize_t
+log_buf_len_store(struct device *dev, struct device_attribute *attr,
+                 const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       int ret;
+       unsigned long addr;
+
+       if (!mdev)
+               return -EINVAL;
+
+       ret = kstrtoul(buf, 16, &addr);
+       if (ret)
+               goto exit;
+
+       mdev->log_buf_len = (int *)addr;
+       ret = count;
+exit:
+       return ret;
+}
+static DEVICE_ATTR_RW(log_buf_len);
+
+static struct attribute *mic_default_attrs[] = {
+       &dev_attr_family.attr,
+       &dev_attr_stepping.attr,
+       &dev_attr_state.attr,
+       &dev_attr_shutdown_status.attr,
+       &dev_attr_cmdline.attr,
+       &dev_attr_firmware.attr,
+       &dev_attr_ramdisk.attr,
+       &dev_attr_bootmode.attr,
+       &dev_attr_log_buf_addr.attr,
+       &dev_attr_log_buf_len.attr,
+
+       NULL
+};
+
+ATTRIBUTE_GROUPS(mic_default);
+
+void mic_sysfs_init(struct mic_device *mdev)
+{
+       mdev->attr_group = mic_default_groups;
+}
diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c
new file mode 100644 (file)
index 0000000..5b8494b
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_smpt.h"
+#include "mic_virtio.h"
+
+/*
+ * Initiates the copies across the PCIe bus from card memory to
+ * a user space buffer.
+ */
+static int mic_virtio_copy_to_user(struct mic_vdev *mvdev,
+               void __user *ubuf, size_t len, u64 addr)
+{
+       int err;
+       void __iomem *dbuf = mvdev->mdev->aper.va + addr;
+       /*
+        * We are copying from IO below an should ideally use something
+        * like copy_to_user_fromio(..) if it existed.
+        */
+       if (copy_to_user(ubuf, dbuf, len)) {
+               err = -EFAULT;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+               goto err;
+       }
+       mvdev->in_bytes += len;
+       err = 0;
+err:
+       return err;
+}
+
+/*
+ * Initiates copies across the PCIe bus from a user space
+ * buffer to card memory.
+ */
+static int mic_virtio_copy_from_user(struct mic_vdev *mvdev,
+               void __user *ubuf, size_t len, u64 addr)
+{
+       int err;
+       void __iomem *dbuf = mvdev->mdev->aper.va + addr;
+       /*
+        * We are copying to IO below and should ideally use something
+        * like copy_from_user_toio(..) if it existed.
+        */
+       if (copy_from_user(dbuf, ubuf, len)) {
+               err = -EFAULT;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+               goto err;
+       }
+       mvdev->out_bytes += len;
+       err = 0;
+err:
+       return err;
+}
+
+#define MIC_VRINGH_READ true
+
+/* The function to call to notify the card about added buffers */
+static void mic_notify(struct vringh *vrh)
+{
+       struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh);
+       struct mic_vdev *mvdev = mvrh->mvdev;
+       s8 db = mvdev->dc->h2c_vdev_db;
+
+       if (db != -1)
+               mvdev->mdev->ops->send_intr(mvdev->mdev, db);
+}
+
+/* Determine the total number of bytes consumed in a VRINGH KIOV */
+static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov)
+{
+       int i;
+       u32 total = iov->consumed;
+
+       for (i = 0; i < iov->i; i++)
+               total += iov->iov[i].iov_len;
+       return total;
+}
+
+/*
+ * Traverse the VRINGH KIOV and issue the APIs to trigger the copies.
+ * This API is heavily based on the vringh_iov_xfer(..) implementation
+ * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..)
+ * and vringh_iov_push_kern(..) directly is because there is no
+ * way to override the VRINGH xfer(..) routines as of v3.10.
+ */
+static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov,
+       void __user *ubuf, size_t len, bool read, size_t *out_len)
+{
+       int ret = 0;
+       size_t partlen, tot_len = 0;
+
+       while (len && iov->i < iov->used) {
+               partlen = min(iov->iov[iov->i].iov_len, len);
+               if (read)
+                       ret = mic_virtio_copy_to_user(mvdev,
+                               ubuf, partlen,
+                               (u64)iov->iov[iov->i].iov_base);
+               else
+                       ret = mic_virtio_copy_from_user(mvdev,
+                               ubuf, partlen,
+                               (u64)iov->iov[iov->i].iov_base);
+               if (ret) {
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       break;
+               }
+               len -= partlen;
+               ubuf += partlen;
+               tot_len += partlen;
+               iov->consumed += partlen;
+               iov->iov[iov->i].iov_len -= partlen;
+               iov->iov[iov->i].iov_base += partlen;
+               if (!iov->iov[iov->i].iov_len) {
+                       /* Fix up old iov element then increment. */
+                       iov->iov[iov->i].iov_len = iov->consumed;
+                       iov->iov[iov->i].iov_base -= iov->consumed;
+
+                       iov->consumed = 0;
+                       iov->i++;
+               }
+       }
+       *out_len = tot_len;
+       return ret;
+}
+
+/*
+ * Use the standard VRINGH infrastructure in the kernel to fetch new
+ * descriptors, initiate the copies and update the used ring.
+ */
+static int _mic_virtio_copy(struct mic_vdev *mvdev,
+       struct mic_copy_desc *copy)
+{
+       int ret = 0, iovcnt = copy->iovcnt;
+       struct iovec iov;
+       struct iovec __user *u_iov = copy->iov;
+       void __user *ubuf = NULL;
+       struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx];
+       struct vringh_kiov *riov = &mvr->riov;
+       struct vringh_kiov *wiov = &mvr->wiov;
+       struct vringh *vrh = &mvr->vrh;
+       u16 *head = &mvr->head;
+       struct mic_vring *vr = &mvr->vring;
+       size_t len = 0, out_len;
+
+       copy->out_len = 0;
+       /* Fetch a new IOVEC if all previous elements have been processed */
+       if (riov->i == riov->used && wiov->i == wiov->used) {
+               ret = vringh_getdesc_kern(vrh, riov, wiov,
+                               head, GFP_KERNEL);
+               /* Check if there are available descriptors */
+               if (ret <= 0)
+                       return ret;
+       }
+       while (iovcnt) {
+               if (!len) {
+                       /* Copy over a new iovec from user space. */
+                       ret = copy_from_user(&iov, u_iov, sizeof(*u_iov));
+                       if (ret) {
+                               ret = -EINVAL;
+                               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                                       __func__, __LINE__, ret);
+                               break;
+                       }
+                       len = iov.iov_len;
+                       ubuf = iov.iov_base;
+               }
+               /* Issue all the read descriptors first */
+               ret = mic_vringh_copy(mvdev, riov, ubuf, len,
+                       MIC_VRINGH_READ, &out_len);
+               if (ret) {
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       break;
+               }
+               len -= out_len;
+               ubuf += out_len;
+               copy->out_len += out_len;
+               /* Issue the write descriptors next */
+               ret = mic_vringh_copy(mvdev, wiov, ubuf, len,
+                       !MIC_VRINGH_READ, &out_len);
+               if (ret) {
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       break;
+               }
+               len -= out_len;
+               ubuf += out_len;
+               copy->out_len += out_len;
+               if (!len) {
+                       /* One user space iovec is now completed */
+                       iovcnt--;
+                       u_iov++;
+               }
+               /* Exit loop if all elements in KIOVs have been processed. */
+               if (riov->i == riov->used && wiov->i == wiov->used)
+                       break;
+       }
+       /*
+        * Update the used ring if a descriptor was available and some data was
+        * copied in/out and the user asked for a used ring update.
+        */
+       if (*head != USHRT_MAX && copy->out_len && copy->update_used) {
+               u32 total = 0;
+
+               /* Determine the total data consumed */
+               total += mic_vringh_iov_consumed(riov);
+               total += mic_vringh_iov_consumed(wiov);
+               vringh_complete_kern(vrh, *head, total);
+               *head = USHRT_MAX;
+               if (vringh_need_notify_kern(vrh) > 0)
+                       vringh_notify(vrh);
+               vringh_kiov_cleanup(riov);
+               vringh_kiov_cleanup(wiov);
+               /* Update avail idx for user space */
+               vr->info->avail_idx = vrh->last_avail_idx;
+       }
+       return ret;
+}
+
+static inline int mic_verify_copy_args(struct mic_vdev *mvdev,
+               struct mic_copy_desc *copy)
+{
+       if (copy->vr_idx >= mvdev->dd->num_vq) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EINVAL);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* Copy a specified number of virtio descriptors in a chain */
+int mic_virtio_copy_desc(struct mic_vdev *mvdev,
+               struct mic_copy_desc *copy)
+{
+       int err;
+       struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx];
+
+       err = mic_verify_copy_args(mvdev, copy);
+       if (err)
+               return err;
+
+       mutex_lock(&mvr->vr_mutex);
+       if (!mic_vdevup(mvdev)) {
+               err = -ENODEV;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+               goto err;
+       }
+       err = _mic_virtio_copy(mvdev, copy);
+       if (err) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+       }
+err:
+       mutex_unlock(&mvr->vr_mutex);
+       return err;
+}
+
+static void mic_virtio_init_post(struct mic_vdev *mvdev)
+{
+       struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd);
+       int i;
+
+       for (i = 0; i < mvdev->dd->num_vq; i++) {
+               if (!le64_to_cpu(vqconfig[i].used_address)) {
+                       dev_warn(mic_dev(mvdev), "used_address zero??\n");
+                       continue;
+               }
+               mvdev->mvr[i].vrh.vring.used =
+                       mvdev->mdev->aper.va +
+                       le64_to_cpu(vqconfig[i].used_address);
+       }
+
+       mvdev->dc->used_address_updated = 0;
+
+       dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n",
+               __func__, mvdev->virtio_id);
+}
+
+static inline void mic_virtio_device_reset(struct mic_vdev *mvdev)
+{
+       int i;
+
+       dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n",
+               __func__, mvdev->dd->status, mvdev->virtio_id);
+
+       for (i = 0; i < mvdev->dd->num_vq; i++)
+               /*
+                * Avoid lockdep false positive. The + 1 is for the mic
+                * mutex which is held in the reset devices code path.
+                */
+               mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1);
+
+       /* 0 status means "reset" */
+       mvdev->dd->status = 0;
+       mvdev->dc->vdev_reset = 0;
+       mvdev->dc->host_ack = 1;
+
+       for (i = 0; i < mvdev->dd->num_vq; i++) {
+               struct vringh *vrh = &mvdev->mvr[i].vrh;
+               mvdev->mvr[i].vring.info->avail_idx = 0;
+               vrh->completed = 0;
+               vrh->last_avail_idx = 0;
+               vrh->last_used_idx = 0;
+       }
+
+       for (i = 0; i < mvdev->dd->num_vq; i++)
+               mutex_unlock(&mvdev->mvr[i].vr_mutex);
+}
+
+void mic_virtio_reset_devices(struct mic_device *mdev)
+{
+       struct list_head *pos, *tmp;
+       struct mic_vdev *mvdev;
+
+       dev_dbg(mdev->sdev->parent, "%s\n",  __func__);
+
+       list_for_each_safe(pos, tmp, &mdev->vdev_list) {
+               mvdev = list_entry(pos, struct mic_vdev, list);
+               mic_virtio_device_reset(mvdev);
+               mvdev->poll_wake = 1;
+               wake_up(&mvdev->waitq);
+       }
+}
+
+void mic_bh_handler(struct work_struct *work)
+{
+       struct mic_vdev *mvdev = container_of(work, struct mic_vdev,
+                       virtio_bh_work);
+
+       if (mvdev->dc->used_address_updated)
+               mic_virtio_init_post(mvdev);
+
+       if (mvdev->dc->vdev_reset)
+               mic_virtio_device_reset(mvdev);
+
+       mvdev->poll_wake = 1;
+       wake_up(&mvdev->waitq);
+}
+
+static irqreturn_t mic_virtio_intr_handler(int irq, void *data)
+{
+       struct mic_vdev *mvdev = data;
+       struct mic_device *mdev = mvdev->mdev;
+
+       mdev->ops->ack_interrupt(mdev);
+       schedule_work(&mvdev->virtio_bh_work);
+       return IRQ_HANDLED;
+}
+
+int mic_virtio_config_change(struct mic_vdev *mvdev,
+                       void __user *argp)
+{
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
+       int ret = 0, retry = 100, i;
+       struct mic_bootparam *bootparam = mvdev->mdev->dp;
+       s8 db = bootparam->h2c_config_db;
+
+       mutex_lock(&mvdev->mdev->mic_mutex);
+       for (i = 0; i < mvdev->dd->num_vq; i++)
+               mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1);
+
+       if (db == -1 || mvdev->dd->type == -1) {
+               ret = -EIO;
+               goto exit;
+       }
+
+       if (copy_from_user(mic_vq_configspace(mvdev->dd),
+                          argp, mvdev->dd->config_len)) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EFAULT);
+               ret = -EFAULT;
+               goto exit;
+       }
+       mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED;
+       mvdev->mdev->ops->send_intr(mvdev->mdev, db);
+
+       for (i = retry; i--;) {
+               ret = wait_event_timeout(wake,
+                       mvdev->dc->guest_ack, msecs_to_jiffies(100));
+               if (ret)
+                       break;
+       }
+
+       dev_dbg(mic_dev(mvdev),
+               "%s %d retry: %d\n", __func__, __LINE__, retry);
+       mvdev->dc->config_change = 0;
+       mvdev->dc->guest_ack = 0;
+exit:
+       for (i = 0; i < mvdev->dd->num_vq; i++)
+               mutex_unlock(&mvdev->mvr[i].vr_mutex);
+       mutex_unlock(&mvdev->mdev->mic_mutex);
+       return ret;
+}
+
+static int mic_copy_dp_entry(struct mic_vdev *mvdev,
+                                       void __user *argp,
+                                       __u8 *type,
+                                       struct mic_device_desc **devpage)
+{
+       struct mic_device *mdev = mvdev->mdev;
+       struct mic_device_desc dd, *dd_config, *devp;
+       struct mic_vqconfig *vqconfig;
+       int ret = 0, i;
+       bool slot_found = false;
+
+       if (copy_from_user(&dd, argp, sizeof(dd))) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EFAULT);
+               return -EFAULT;
+       }
+
+       if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE ||
+           dd.num_vq > MIC_MAX_VRINGS) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EINVAL);
+               return -EINVAL;
+       }
+
+       dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL);
+       if (dd_config == NULL) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -ENOMEM);
+               return -ENOMEM;
+       }
+       if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) {
+               ret = -EFAULT;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, ret);
+               goto exit;
+       }
+
+       vqconfig = mic_vq_config(dd_config);
+       for (i = 0; i < dd.num_vq; i++) {
+               if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) {
+                       ret =  -EINVAL;
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       goto exit;
+               }
+       }
+
+       /* Find the first free device page entry */
+       for (i = mic_aligned_size(struct mic_bootparam);
+               i < MIC_DP_SIZE - mic_total_desc_size(dd_config);
+               i += mic_total_desc_size(devp)) {
+               devp = mdev->dp + i;
+               if (devp->type == 0 || devp->type == -1) {
+                       slot_found = true;
+                       break;
+               }
+       }
+       if (!slot_found) {
+               ret =  -EINVAL;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, ret);
+               goto exit;
+       }
+       /*
+        * Save off the type before doing the memcpy. Type will be set in the
+        * end after completing all initialization for the new device.
+        */
+       *type = dd_config->type;
+       dd_config->type = 0;
+       memcpy(devp, dd_config, mic_desc_size(dd_config));
+
+       *devpage = devp;
+exit:
+       kfree(dd_config);
+       return ret;
+}
+
+static void mic_init_device_ctrl(struct mic_vdev *mvdev,
+                               struct mic_device_desc *devpage)
+{
+       struct mic_device_ctrl *dc;
+
+       dc = (void *)devpage + mic_aligned_desc_size(devpage);
+
+       dc->config_change = 0;
+       dc->guest_ack = 0;
+       dc->vdev_reset = 0;
+       dc->host_ack = 0;
+       dc->used_address_updated = 0;
+       dc->c2h_vdev_db = -1;
+       dc->h2c_vdev_db = -1;
+       mvdev->dc = dc;
+}
+
+int mic_virtio_add_device(struct mic_vdev *mvdev,
+                       void __user *argp)
+{
+       struct mic_device *mdev = mvdev->mdev;
+       struct mic_device_desc *dd = NULL;
+       struct mic_vqconfig *vqconfig;
+       int vr_size, i, j, ret;
+       u8 type = 0;
+       s8 db;
+       char irqname[10];
+       struct mic_bootparam *bootparam = mdev->dp;
+       u16 num;
+
+       mutex_lock(&mdev->mic_mutex);
+
+       ret = mic_copy_dp_entry(mvdev, argp, &type, &dd);
+       if (ret) {
+               mutex_unlock(&mdev->mic_mutex);
+               return ret;
+       }
+
+       mic_init_device_ctrl(mvdev, dd);
+
+       mvdev->dd = dd;
+       mvdev->virtio_id = type;
+       vqconfig = mic_vq_config(dd);
+       INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler);
+
+       for (i = 0; i < dd->num_vq; i++) {
+               struct mic_vringh *mvr = &mvdev->mvr[i];
+               struct mic_vring *vr = &mvdev->mvr[i].vring;
+               num = le16_to_cpu(vqconfig[i].num);
+               mutex_init(&mvr->vr_mutex);
+               vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) +
+                       sizeof(struct _mic_vring_info));
+               vr->va = (void *)
+                       __get_free_pages(GFP_KERNEL | __GFP_ZERO,
+                                        get_order(vr_size));
+               if (!vr->va) {
+                       ret = -ENOMEM;
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       goto err;
+               }
+               vr->len = vr_size;
+               vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN);
+               vr->info->magic = MIC_MAGIC + mvdev->virtio_id + i;
+               vqconfig[i].address = mic_map_single(mdev,
+                       vr->va, vr_size);
+               if (mic_map_error(vqconfig[i].address)) {
+                       free_pages((unsigned long)vr->va, get_order(vr_size));
+                       ret = -ENOMEM;
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       goto err;
+               }
+               vqconfig[i].address = cpu_to_le64(vqconfig[i].address);
+
+               vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN);
+               ret = vringh_init_kern(&mvr->vrh,
+                       *(u32 *)mic_vq_features(mvdev->dd), num, false,
+                       vr->vr.desc, vr->vr.avail, vr->vr.used);
+               if (ret) {
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       goto err;
+               }
+               vringh_kiov_init(&mvr->riov, NULL, 0);
+               vringh_kiov_init(&mvr->wiov, NULL, 0);
+               mvr->head = USHRT_MAX;
+               mvr->mvdev = mvdev;
+               mvr->vrh.notify = mic_notify;
+               dev_dbg(mdev->sdev->parent,
+                       "%s %d index %d va %p info %p vr_size 0x%x\n",
+                       __func__, __LINE__, i, vr->va, vr->info, vr_size);
+       }
+
+       snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id,
+                mvdev->virtio_id);
+       mvdev->virtio_db = mic_next_db(mdev);
+       mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler,
+                       irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB);
+       if (IS_ERR(mvdev->virtio_cookie)) {
+               ret = PTR_ERR(mvdev->virtio_cookie);
+               dev_dbg(mdev->sdev->parent, "request irq failed\n");
+               goto err;
+       }
+
+       mvdev->dc->c2h_vdev_db = mvdev->virtio_db;
+
+       list_add_tail(&mvdev->list, &mdev->vdev_list);
+       /*
+        * Order the type update with previous stores. This write barrier
+        * is paired with the corresponding read barrier before the uncached
+        * system memory read of the type, on the card while scanning the
+        * device page.
+        */
+       smp_wmb();
+       dd->type = type;
+
+       dev_dbg(mdev->sdev->parent, "Added virtio device id %d\n", dd->type);
+
+       db = bootparam->h2c_config_db;
+       if (db != -1)
+               mdev->ops->send_intr(mdev, db);
+       mutex_unlock(&mdev->mic_mutex);
+       return 0;
+err:
+       vqconfig = mic_vq_config(dd);
+       for (j = 0; j < i; j++) {
+               struct mic_vringh *mvr = &mvdev->mvr[j];
+               mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address),
+                                mvr->vring.len);
+               free_pages((unsigned long)mvr->vring.va,
+                          get_order(mvr->vring.len));
+       }
+       mutex_unlock(&mdev->mic_mutex);
+       return ret;
+}
+
+void mic_virtio_del_device(struct mic_vdev *mvdev)
+{
+       struct list_head *pos, *tmp;
+       struct mic_vdev *tmp_mvdev;
+       struct mic_device *mdev = mvdev->mdev;
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
+       int i, ret, retry = 100;
+       struct mic_vqconfig *vqconfig;
+       struct mic_bootparam *bootparam = mdev->dp;
+       s8 db;
+
+       mutex_lock(&mdev->mic_mutex);
+       db = bootparam->h2c_config_db;
+       if (db == -1)
+               goto skip_hot_remove;
+       dev_dbg(mdev->sdev->parent,
+               "Requesting hot remove id %d\n", mvdev->virtio_id);
+       mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE;
+       mdev->ops->send_intr(mdev, db);
+       for (i = retry; i--;) {
+               ret = wait_event_timeout(wake,
+                       mvdev->dc->guest_ack, msecs_to_jiffies(100));
+               if (ret)
+                       break;
+       }
+       dev_dbg(mdev->sdev->parent,
+               "Device id %d config_change %d guest_ack %d\n",
+               mvdev->virtio_id, mvdev->dc->config_change,
+               mvdev->dc->guest_ack);
+       mvdev->dc->config_change = 0;
+       mvdev->dc->guest_ack = 0;
+skip_hot_remove:
+       mic_free_irq(mdev, mvdev->virtio_cookie, mvdev);
+       flush_work(&mvdev->virtio_bh_work);
+       vqconfig = mic_vq_config(mvdev->dd);
+       for (i = 0; i < mvdev->dd->num_vq; i++) {
+               struct mic_vringh *mvr = &mvdev->mvr[i];
+               vringh_kiov_cleanup(&mvr->riov);
+               vringh_kiov_cleanup(&mvr->wiov);
+               mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address),
+                                mvr->vring.len);
+               free_pages((unsigned long)mvr->vring.va,
+                          get_order(mvr->vring.len));
+       }
+
+       list_for_each_safe(pos, tmp, &mdev->vdev_list) {
+               tmp_mvdev = list_entry(pos, struct mic_vdev, list);
+               if (tmp_mvdev == mvdev) {
+                       list_del(pos);
+                       dev_dbg(mdev->sdev->parent,
+                               "Removing virtio device id %d\n",
+                               mvdev->virtio_id);
+                       break;
+               }
+       }
+       /*
+        * Order the type update with previous stores. This write barrier
+        * is paired with the corresponding read barrier before the uncached
+        * system memory read of the type, on the card while scanning the
+        * device page.
+        */
+       smp_wmb();
+       mvdev->dd->type = -1;
+       mutex_unlock(&mdev->mic_mutex);
+}
diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h
new file mode 100644 (file)
index 0000000..184f3c8
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef MIC_VIRTIO_H
+#define MIC_VIRTIO_H
+
+#include <linux/virtio_config.h>
+#include <linux/mic_ioctl.h>
+
+/*
+ * Note on endianness.
+ * 1. Host can be both BE or LE
+ * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail
+ *    rings and ioreadXX/iowriteXX to access used ring.
+ * 3. Device page exposed by host to guest contains LE values. Guest
+ *    accesses these using ioreadXX/iowriteXX etc. This way in general we
+ *    obey the virtio spec according to which guest works with native
+ *    endianness and host is aware of guest endianness and does all
+ *    required endianness conversion.
+ * 4. Data provided from user space to guest (in ADD_DEVICE and
+ *    CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be
+ *    in guest endianness.
+ */
+
+/**
+ * struct mic_vringh - Virtio ring host information.
+ *
+ * @vring: The MIC vring used for setting up user space mappings.
+ * @vrh: The host VRINGH used for accessing the card vrings.
+ * @riov: The VRINGH read kernel IOV.
+ * @wiov: The VRINGH write kernel IOV.
+ * @head: The VRINGH head index address passed to vringh_getdesc_kern(..).
+ * @vr_mutex: Mutex for synchronizing access to the VRING.
+ * @mvdev: Back pointer to MIC virtio device for vringh_notify(..).
+ */
+struct mic_vringh {
+       struct mic_vring vring;
+       struct vringh vrh;
+       struct vringh_kiov riov;
+       struct vringh_kiov wiov;
+       u16 head;
+       struct mutex vr_mutex;
+       struct mic_vdev *mvdev;
+};
+
+/**
+ * struct mic_vdev - Host information for a card Virtio device.
+ *
+ * @virtio_id - Virtio device id.
+ * @waitq - Waitqueue to allow ring3 apps to poll.
+ * @mdev - Back pointer to host MIC device.
+ * @poll_wake - Used for waking up threads blocked in poll.
+ * @out_bytes - Debug stats for number of bytes copied from host to card.
+ * @in_bytes - Debug stats for number of bytes copied from card to host.
+ * @mvr - Store per VRING data structures.
+ * @virtio_bh_work - Work struct used to schedule virtio bottom half handling.
+ * @dd - Virtio device descriptor.
+ * @dc - Virtio device control fields.
+ * @list - List of Virtio devices.
+ * @virtio_db - The doorbell used by the card to interrupt the host.
+ * @virtio_cookie - The cookie returned while requesting interrupts.
+ */
+struct mic_vdev {
+       int virtio_id;
+       wait_queue_head_t waitq;
+       struct mic_device *mdev;
+       int poll_wake;
+       unsigned long out_bytes;
+       unsigned long in_bytes;
+       struct mic_vringh mvr[MIC_MAX_VRINGS];
+       struct work_struct virtio_bh_work;
+       struct mic_device_desc *dd;
+       struct mic_device_ctrl *dc;
+       struct list_head list;
+       int virtio_db;
+       struct mic_irq *virtio_cookie;
+};
+
+void mic_virtio_uninit(struct mic_device *mdev);
+int mic_virtio_add_device(struct mic_vdev *mvdev,
+                       void __user *argp);
+void mic_virtio_del_device(struct mic_vdev *mvdev);
+int mic_virtio_config_change(struct mic_vdev *mvdev,
+                       void __user *argp);
+int mic_virtio_copy_desc(struct mic_vdev *mvdev,
+       struct mic_copy_desc *request);
+void mic_virtio_reset_devices(struct mic_device *mdev);
+void mic_bh_handler(struct work_struct *work);
+
+/* Helper API to obtain the MIC PCIe device */
+static inline struct device *mic_dev(struct mic_vdev *mvdev)
+{
+       return mvdev->mdev->sdev->parent;
+}
+
+/* Helper API to check if a virtio device is initialized */
+static inline int mic_vdev_inited(struct mic_vdev *mvdev)
+{
+       /* Device has not been created yet */
+       if (!mvdev->dd || !mvdev->dd->type) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EINVAL);
+               return -EINVAL;
+       }
+
+       /* Device has been removed/deleted */
+       if (mvdev->dd->type == -1) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -ENODEV);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/* Helper API to check if a virtio device is running */
+static inline bool mic_vdevup(struct mic_vdev *mvdev)
+{
+       return !!mvdev->dd->status;
+}
+#endif
diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c
new file mode 100644 (file)
index 0000000..81e9541
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_x100.h"
+#include "mic_smpt.h"
+
+/**
+ * mic_x100_write_spad - write to the scratchpad register
+ * @mdev: pointer to mic_device instance
+ * @idx: index to the scratchpad register, 0 based
+ * @val: the data value to put into the register
+ *
+ * This function allows writing of a 32bit value to the indexed scratchpad
+ * register.
+ *
+ * RETURNS: none.
+ */
+static void
+mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val)
+{
+       dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n",
+               val, idx);
+       mic_mmio_write(&mdev->mmio, val,
+                      MIC_X100_SBOX_BASE_ADDRESS +
+                      MIC_X100_SBOX_SPAD0 + idx * 4);
+}
+
+/**
+ * mic_x100_read_spad - read from the scratchpad register
+ * @mdev: pointer to mic_device instance
+ * @idx: index to scratchpad register, 0 based
+ *
+ * This function allows reading of the 32bit scratchpad register.
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static u32
+mic_x100_read_spad(struct mic_device *mdev, unsigned int idx)
+{
+       u32 val = mic_mmio_read(&mdev->mmio,
+               MIC_X100_SBOX_BASE_ADDRESS +
+               MIC_X100_SBOX_SPAD0 + idx * 4);
+
+       dev_dbg(mdev->sdev->parent,
+               "Reading 0x%x from scratch pad index %d\n", val, idx);
+       return val;
+}
+
+/**
+ * mic_x100_enable_interrupts - Enable interrupts.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_enable_interrupts(struct mic_device *mdev)
+{
+       u32 reg;
+       struct mic_mw *mw = &mdev->mmio;
+       u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0;
+       u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0;
+
+       reg = mic_mmio_read(mw, sice0);
+       reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff);
+       mic_mmio_write(mw, reg, sice0);
+
+       /*
+        * Enable auto-clear when enabling interrupts. Applicable only for
+        * MSI-x. Legacy and MSI mode cannot have auto-clear enabled.
+        */
+       if (mdev->irq_info.num_vectors > 1) {
+               reg = mic_mmio_read(mw, siac0);
+               reg |= MIC_X100_SBOX_DBR_BITS(0xf) |
+                       MIC_X100_SBOX_DMA_BITS(0xff);
+               mic_mmio_write(mw, reg, siac0);
+       }
+}
+
+/**
+ * mic_x100_disable_interrupts - Disable interrupts.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_disable_interrupts(struct mic_device *mdev)
+{
+       u32 reg;
+       struct mic_mw *mw = &mdev->mmio;
+       u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0;
+       u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0;
+       u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0;
+
+       reg = mic_mmio_read(mw, sice0);
+       mic_mmio_write(mw, reg, sicc0);
+
+       if (mdev->irq_info.num_vectors > 1) {
+               reg = mic_mmio_read(mw, siac0);
+               reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) |
+                       MIC_X100_SBOX_DMA_BITS(0xff));
+               mic_mmio_write(mw, reg, siac0);
+       }
+}
+
+/**
+ * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_send_sbox_intr(struct mic_device *mdev,
+                       int doorbell)
+{
+       struct mic_mw *mw = &mdev->mmio;
+       u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8;
+       u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS +
+                                       apic_icr_offset);
+
+       /* for MIC we need to make sure we "hit" the send_icr bit (13) */
+       apicicr_low = (apicicr_low | (1 << 13));
+
+       /* Ensure that the interrupt is ordered w.r.t. previous stores. */
+       wmb();
+       mic_mmio_write(mw, apicicr_low,
+                      MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset);
+}
+
+/**
+ * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_send_rdmasr_intr(struct mic_device *mdev,
+                       int doorbell)
+{
+       int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2);
+       /* Ensure that the interrupt is ordered w.r.t. previous stores. */
+       wmb();
+       mic_mmio_write(&mdev->mmio, 0,
+                      MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset);
+}
+
+/**
+ * __mic_x100_send_intr - Send interrupt to MIC.
+ * @mdev: pointer to mic_device instance
+ * @doorbell: doorbell number.
+ */
+static void mic_x100_send_intr(struct mic_device *mdev, int doorbell)
+{
+       int rdmasr_db;
+       if (doorbell < MIC_X100_NUM_SBOX_IRQ) {
+               mic_x100_send_sbox_intr(mdev, doorbell);
+       } else {
+               rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ +
+                       MIC_X100_RDMASR_IRQ_BASE;
+               mic_x100_send_rdmasr_intr(mdev, rdmasr_db);
+       }
+}
+
+/**
+ * mic_ack_interrupt - Device specific interrupt handling.
+ * @mdev: pointer to mic_device instance
+ *
+ * Returns: bitmask of doorbell events triggered.
+ */
+static u32 mic_x100_ack_interrupt(struct mic_device *mdev)
+{
+       u32 reg = 0;
+       struct mic_mw *mw = &mdev->mmio;
+       u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0;
+
+       /* Clear pending bit array. */
+       if (MIC_A0_STEP == mdev->stepping)
+               mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS +
+                       MIC_X100_SBOX_MSIXPBACR);
+
+       if (mdev->irq_info.num_vectors <= 1) {
+               reg = mic_mmio_read(mw, sicr0);
+
+               if (unlikely(!reg))
+                       goto done;
+
+               mic_mmio_write(mw, reg, sicr0);
+       }
+
+       if (mdev->stepping >= MIC_B0_STEP)
+               mdev->intr_ops->enable_interrupts(mdev);
+done:
+       return reg;
+}
+
+/**
+ * mic_x100_hw_intr_init - Initialize h/w specific interrupt
+ * information.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_hw_intr_init(struct mic_device *mdev)
+{
+       mdev->intr_info = (struct mic_intr_info *)mic_x100_intr_init;
+}
+
+/**
+ * mic_x100_read_msi_to_src_map - read from the MSI mapping registers
+ * @mdev: pointer to mic_device instance
+ * @idx: index to the mapping register, 0 based
+ *
+ * This function allows reading of the 32bit MSI mapping register.
+ *
+ * RETURNS: The value in the register.
+ */
+static u32
+mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx)
+{
+       return mic_mmio_read(&mdev->mmio,
+               MIC_X100_SBOX_BASE_ADDRESS +
+               MIC_X100_SBOX_MXAR0 + idx * 4);
+}
+
+/**
+ * mic_x100_program_msi_to_src_map - program the MSI mapping registers
+ * @mdev: pointer to mic_device instance
+ * @idx: index to the mapping register, 0 based
+ * @offset: The bit offset in the register that needs to be updated.
+ * @set: boolean specifying if the bit in the specified offset needs
+ * to be set or cleared.
+ *
+ * RETURNS: None.
+ */
+static void
+mic_x100_program_msi_to_src_map(struct mic_device *mdev,
+                               int idx, int offset, bool set)
+{
+       unsigned long reg;
+       struct mic_mw *mw = &mdev->mmio;
+       u32 mxar = MIC_X100_SBOX_BASE_ADDRESS +
+               MIC_X100_SBOX_MXAR0 + idx * 4;
+
+       reg = mic_mmio_read(mw, mxar);
+       if (set)
+               __set_bit(offset, &reg);
+       else
+               __clear_bit(offset, &reg);
+       mic_mmio_write(mw, reg, mxar);
+}
+
+/*
+ * mic_x100_reset_fw_ready - Reset Firmware ready status field.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_reset_fw_ready(struct mic_device *mdev)
+{
+       mdev->ops->write_spad(mdev, MIC_X100_DOWNLOAD_INFO, 0);
+}
+
+/*
+ * mic_x100_is_fw_ready - Check if firmware is ready.
+ * @mdev: pointer to mic_device instance
+ */
+static bool mic_x100_is_fw_ready(struct mic_device *mdev)
+{
+       u32 scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO);
+       return MIC_X100_SPAD2_DOWNLOAD_STATUS(scratch2) ? true : false;
+}
+
+/**
+ * mic_x100_get_apic_id - Get bootstrap APIC ID.
+ * @mdev: pointer to mic_device instance
+ */
+static u32 mic_x100_get_apic_id(struct mic_device *mdev)
+{
+       u32 scratch2 = 0;
+
+       scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO);
+       return MIC_X100_SPAD2_APIC_ID(scratch2);
+}
+
+/**
+ * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_send_firmware_intr(struct mic_device *mdev)
+{
+       u32 apicicr_low;
+       u64 apic_icr_offset = MIC_X100_SBOX_APICICR7;
+       int vector = MIC_X100_BSP_INTERRUPT_VECTOR;
+       struct mic_mw *mw = &mdev->mmio;
+
+       /*
+        * For MIC we need to make sure we "hit"
+        * the send_icr bit (13).
+        */
+       apicicr_low = (vector | (1 << 13));
+
+       mic_mmio_write(mw, mic_x100_get_apic_id(mdev),
+                      MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4);
+
+       /* Ensure that the interrupt is ordered w.r.t. previous stores. */
+       wmb();
+       mic_mmio_write(mw, apicicr_low,
+                      MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset);
+}
+
+/**
+ * mic_x100_hw_reset - Reset the MIC device.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_hw_reset(struct mic_device *mdev)
+{
+       u32 reset_reg;
+       u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR;
+       struct mic_mw *mw = &mdev->mmio;
+
+       /* Ensure that the reset is ordered w.r.t. previous loads and stores */
+       mb();
+       /* Trigger reset */
+       reset_reg = mic_mmio_read(mw, rgcr);
+       reset_reg |= 0x1;
+       mic_mmio_write(mw, reset_reg, rgcr);
+       /*
+        * It seems we really want to delay at least 1 second
+        * after touching reset to prevent a lot of problems.
+        */
+       msleep(1000);
+}
+
+/**
+ * mic_x100_load_command_line - Load command line to MIC.
+ * @mdev: pointer to mic_device instance
+ * @fw: the firmware image
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int
+mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw)
+{
+       u32 len = 0;
+       u32 boot_mem;
+       char *buf;
+       void __iomem *cmd_line_va = mdev->aper.va + mdev->bootaddr + fw->size;
+#define CMDLINE_SIZE 2048
+
+       boot_mem = mdev->aper.len >> 20;
+       buf = kzalloc(CMDLINE_SIZE, GFP_KERNEL);
+       if (!buf) {
+               dev_err(mdev->sdev->parent,
+                       "%s %d allocation failed\n", __func__, __LINE__);
+               return -ENOMEM;
+       }
+       len += snprintf(buf, CMDLINE_SIZE - len,
+               " mem=%dM", boot_mem);
+       if (mdev->cmdline)
+               snprintf(buf + len, CMDLINE_SIZE - len, " %s", mdev->cmdline);
+       memcpy_toio(cmd_line_va, buf, strlen(buf) + 1);
+       kfree(buf);
+       return 0;
+}
+
+/**
+ * mic_x100_load_ramdisk - Load ramdisk to MIC.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int
+mic_x100_load_ramdisk(struct mic_device *mdev)
+{
+       const struct firmware *fw;
+       int rc;
+       struct boot_params __iomem *bp = mdev->aper.va + mdev->bootaddr;
+
+       rc = request_firmware(&fw,
+                       mdev->ramdisk, mdev->sdev->parent);
+       if (rc < 0) {
+               dev_err(mdev->sdev->parent,
+                       "ramdisk request_firmware failed: %d %s\n",
+                       rc, mdev->ramdisk);
+               goto error;
+       }
+       /*
+        * Typically the bootaddr for card OS is 64M
+        * so copy over the ramdisk @ 128M.
+        */
+       memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), fw->data, fw->size);
+       iowrite32(cpu_to_le32(mdev->bootaddr << 1), &bp->hdr.ramdisk_image);
+       iowrite32(cpu_to_le32(fw->size), &bp->hdr.ramdisk_size);
+       release_firmware(fw);
+error:
+       return rc;
+}
+
+/**
+ * mic_x100_get_boot_addr - Get MIC boot address.
+ * @mdev: pointer to mic_device instance
+ *
+ * This function is called during firmware load to determine
+ * the address at which the OS should be downloaded in card
+ * memory i.e. GDDR.
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int
+mic_x100_get_boot_addr(struct mic_device *mdev)
+{
+       u32 scratch2, boot_addr;
+       int rc = 0;
+
+       scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO);
+       boot_addr = MIC_X100_SPAD2_DOWNLOAD_ADDR(scratch2);
+       dev_dbg(mdev->sdev->parent, "%s %d boot_addr 0x%x\n",
+               __func__, __LINE__, boot_addr);
+       if (boot_addr > (1 << 31)) {
+               dev_err(mdev->sdev->parent,
+                       "incorrect bootaddr 0x%x\n",
+                       boot_addr);
+               rc = -EINVAL;
+               goto error;
+       }
+       mdev->bootaddr = boot_addr;
+error:
+       return rc;
+}
+
+/**
+ * mic_x100_load_firmware - Load firmware to MIC.
+ * @mdev: pointer to mic_device instance
+ * @buf: buffer containing boot string including firmware/ramdisk path.
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int
+mic_x100_load_firmware(struct mic_device *mdev, const char *buf)
+{
+       int rc;
+       const struct firmware *fw;
+
+       rc = mic_x100_get_boot_addr(mdev);
+       if (rc)
+               goto error;
+       /* load OS */
+       rc = request_firmware(&fw, mdev->firmware, mdev->sdev->parent);
+       if (rc < 0) {
+               dev_err(mdev->sdev->parent,
+                       "ramdisk request_firmware failed: %d %s\n",
+                       rc, mdev->firmware);
+               goto error;
+       }
+       if (mdev->bootaddr > mdev->aper.len - fw->size) {
+               rc = -EINVAL;
+               dev_err(mdev->sdev->parent, "%s %d rc %d bootaddr 0x%x\n",
+                       __func__, __LINE__, rc, mdev->bootaddr);
+               release_firmware(fw);
+               goto error;
+       }
+       memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size);
+       mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size);
+       if (!strcmp(mdev->bootmode, "elf"))
+               goto done;
+       /* load command line */
+       rc = mic_x100_load_command_line(mdev, fw);
+       if (rc) {
+               dev_err(mdev->sdev->parent, "%s %d rc %d\n",
+                       __func__, __LINE__, rc);
+               goto error;
+       }
+       release_firmware(fw);
+       /* load ramdisk */
+       if (mdev->ramdisk)
+               rc = mic_x100_load_ramdisk(mdev);
+error:
+       dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", __func__, __LINE__, rc);
+done:
+       return rc;
+}
+
+/**
+ * mic_x100_get_postcode - Get postcode status from firmware.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: postcode.
+ */
+static u32 mic_x100_get_postcode(struct mic_device *mdev)
+{
+       return mic_mmio_read(&mdev->mmio, MIC_X100_POSTCODE);
+}
+
+/**
+ * mic_x100_smpt_set - Update an SMPT entry with a DMA address.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: none.
+ */
+static void
+mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index)
+{
+#define SNOOP_ON       (0 << 0)
+#define SNOOP_OFF      (1 << 0)
+/*
+ * Sbox Smpt Reg Bits:
+ * Bits        31:2    Host address
+ * Bits        1       RSVD
+ * Bits        0       No snoop
+ */
+#define BUILD_SMPT(NO_SNOOP, HOST_ADDR)  \
+       (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01))
+
+       uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON,
+                       dma_addr >> mdev->smpt->info.page_shift);
+       mic_mmio_write(&mdev->mmio, smpt_reg_val,
+                      MIC_X100_SBOX_BASE_ADDRESS +
+                      MIC_X100_SBOX_SMPT00 + (4 * index));
+}
+
+/**
+ * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: none.
+ */
+static void mic_x100_smpt_hw_init(struct mic_device *mdev)
+{
+       struct mic_smpt_hw_info *info = &mdev->smpt->info;
+
+       info->num_reg = 32;
+       info->page_shift = 34;
+       info->page_size = (1ULL << info->page_shift);
+       info->base = 0x8000000000ULL;
+}
+
+struct mic_smpt_ops mic_x100_smpt_ops = {
+       .init = mic_x100_smpt_hw_init,
+       .set = mic_x100_smpt_set,
+};
+
+struct mic_hw_ops mic_x100_ops = {
+       .aper_bar = MIC_X100_APER_BAR,
+       .mmio_bar = MIC_X100_MMIO_BAR,
+       .read_spad = mic_x100_read_spad,
+       .write_spad = mic_x100_write_spad,
+       .send_intr = mic_x100_send_intr,
+       .ack_interrupt = mic_x100_ack_interrupt,
+       .reset = mic_x100_hw_reset,
+       .reset_fw_ready = mic_x100_reset_fw_ready,
+       .is_fw_ready = mic_x100_is_fw_ready,
+       .send_firmware_intr = mic_x100_send_firmware_intr,
+       .load_mic_fw = mic_x100_load_firmware,
+       .get_postcode = mic_x100_get_postcode,
+};
+
+struct mic_hw_intr_ops mic_x100_intr_ops = {
+       .intr_init = mic_x100_hw_intr_init,
+       .enable_interrupts = mic_x100_enable_interrupts,
+       .disable_interrupts = mic_x100_disable_interrupts,
+       .program_msi_to_src_map = mic_x100_program_msi_to_src_map,
+       .read_msi_to_src_map = mic_x100_read_msi_to_src_map,
+};
diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h
new file mode 100644 (file)
index 0000000..8b7daa1
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_X100_HW_H_
+#define _MIC_X100_HW_H_
+
+#define MIC_X100_PCI_DEVICE_2250 0x2250
+#define MIC_X100_PCI_DEVICE_2251 0x2251
+#define MIC_X100_PCI_DEVICE_2252 0x2252
+#define MIC_X100_PCI_DEVICE_2253 0x2253
+#define MIC_X100_PCI_DEVICE_2254 0x2254
+#define MIC_X100_PCI_DEVICE_2255 0x2255
+#define MIC_X100_PCI_DEVICE_2256 0x2256
+#define MIC_X100_PCI_DEVICE_2257 0x2257
+#define MIC_X100_PCI_DEVICE_2258 0x2258
+#define MIC_X100_PCI_DEVICE_2259 0x2259
+#define MIC_X100_PCI_DEVICE_225a 0x225a
+#define MIC_X100_PCI_DEVICE_225b 0x225b
+#define MIC_X100_PCI_DEVICE_225c 0x225c
+#define MIC_X100_PCI_DEVICE_225d 0x225d
+#define MIC_X100_PCI_DEVICE_225e 0x225e
+
+#define MIC_X100_APER_BAR 0
+#define MIC_X100_MMIO_BAR 4
+
+#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000
+#define MIC_X100_SBOX_SPAD0 0x0000AB20
+#define MIC_X100_SBOX_SICR0_DBR(x) ((x) & 0xf)
+#define MIC_X100_SBOX_SICR0_DMA(x) (((x) >> 8) & 0xff)
+#define MIC_X100_SBOX_SICE0_DBR(x) ((x) & 0xf)
+#define MIC_X100_SBOX_DBR_BITS(x) ((x) & 0xf)
+#define MIC_X100_SBOX_SICE0_DMA(x) (((x) >> 8) & 0xff)
+#define MIC_X100_SBOX_DMA_BITS(x) (((x) & 0xff) << 8)
+
+#define MIC_X100_SBOX_APICICR0 0x0000A9D0
+#define MIC_X100_SBOX_SICR0 0x00009004
+#define MIC_X100_SBOX_SICE0 0x0000900C
+#define MIC_X100_SBOX_SICC0 0x00009010
+#define MIC_X100_SBOX_SIAC0 0x00009014
+#define MIC_X100_SBOX_MSIXPBACR 0x00009084
+#define MIC_X100_SBOX_MXAR0 0x00009044
+#define MIC_X100_SBOX_SMPT00 0x00003100
+#define MIC_X100_SBOX_RDMASR0 0x0000B180
+
+#define MIC_X100_DOORBELL_IDX_START 0
+#define MIC_X100_NUM_DOORBELL 4
+#define MIC_X100_DMA_IDX_START 8
+#define MIC_X100_NUM_DMA 8
+#define MIC_X100_ERR_IDX_START 30
+#define MIC_X100_NUM_ERR 1
+
+#define MIC_X100_NUM_SBOX_IRQ 8
+#define MIC_X100_NUM_RDMASR_IRQ 8
+#define MIC_X100_RDMASR_IRQ_BASE 17
+#define MIC_X100_SPAD2_DOWNLOAD_STATUS(x) ((x) & 0x1)
+#define MIC_X100_SPAD2_APIC_ID(x)      (((x) >> 1) & 0x1ff)
+#define MIC_X100_SPAD2_DOWNLOAD_ADDR(x) ((x) & 0xfffff000)
+#define MIC_X100_SBOX_APICICR7 0x0000AA08
+#define MIC_X100_SBOX_RGCR 0x00004010
+#define MIC_X100_SBOX_SDBIC0 0x0000CC90
+#define MIC_X100_DOWNLOAD_INFO 2
+#define MIC_X100_FW_SIZE 5
+#define MIC_X100_POSTCODE 0x242c
+
+static const u16 mic_x100_intr_init[] = {
+               MIC_X100_DOORBELL_IDX_START,
+               MIC_X100_DMA_IDX_START,
+               MIC_X100_ERR_IDX_START,
+               MIC_X100_NUM_DOORBELL,
+               MIC_X100_NUM_DMA,
+               MIC_X100_NUM_ERR,
+};
+
+/* Host->Card(bootstrap) Interrupt Vector */
+#define MIC_X100_BSP_INTERRUPT_VECTOR 229
+
+extern struct mic_hw_ops mic_x100_ops;
+extern struct mic_smpt_ops mic_x100_smpt_ops;
+extern struct mic_hw_intr_ops mic_x100_intr_ops;
+
+#endif
index 68b7c773d2cf472e23395ec563a7ed3abfbb9c9c..30754927fd80d584881b3ce5475ddef874bb353c 100644 (file)
@@ -395,7 +395,7 @@ static int phantom_probe(struct pci_dev *pdev,
        iowrite32(0, pht->caddr + PHN_IRQCTL);
        ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
        retval = request_irq(pdev->irq, phantom_isr,
-                       IRQF_SHARED | IRQF_DISABLED, "phantom", pht);
+                       IRQF_SHARED, "phantom", pht);
        if (retval) {
                dev_err(&pdev->dev, "can't establish ISR\n");
                goto err_unmo;
index f84ff0c060358d2d4e2fa6e78d070fd385f0eaa4..eda38cbe85307edd67863122e24451d269d66866 100644 (file)
@@ -892,7 +892,6 @@ static void pti_pci_remove(struct pci_dev *pdev)
        }
 
        iounmap(drv_data->pti_ioaddr);
-       pci_set_drvdata(pdev, NULL);
        kfree(drv_data);
        pci_release_region(pdev, 1);
        pci_disable_device(pdev);
index 9b237221bc4e8abf19391886ee6465065db2e16a..83da711ce9f13688660192f55fac62571961ec93 100644 (file)
@@ -22,9 +22,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/spi/spi.h>
-
-#define DAC7512_DRV_NAME       "dac7512"
-#define DRIVER_VERSION         "1.0"
+#include <linux/of.h>
 
 static ssize_t dac7512_store_val(struct device *dev,
                                 struct device_attribute *attr,
@@ -75,13 +73,29 @@ static int dac7512_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct spi_device_id dac7512_id_table[] = {
+       { "dac7512", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, dac7512_id_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id dac7512_of_match[] = {
+       { .compatible = "ti,dac7512", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, dac7512_of_match);
+#endif
+
 static struct spi_driver dac7512_driver = {
        .driver = {
-               .name   = DAC7512_DRV_NAME,
+               .name   = "dac7512",
                .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(dac7512_of_match),
        },
        .probe  = dac7512_probe,
        .remove = dac7512_remove,
+       .id_table = dac7512_id_table,
 };
 
 module_spi_driver(dac7512_driver);
@@ -89,4 +103,3 @@ module_spi_driver(dac7512_driver);
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
 MODULE_DESCRIPTION("DAC7512 16-bit DAC");
 MODULE_LICENSE("GPL v2");
-MODULE_VERSION(DRIVER_VERSION);
index f8d6654391e5df35f13ec7b7e825baf3710baee6..a606c8901e1859099bf387e8a7d1f8b07b0f3cd0 100644 (file)
@@ -356,8 +356,10 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
        pci_set_drvdata(dev, fm);
 
        fm->addr = pci_ioremap_bar(dev, 0);
-       if (!fm->addr)
+       if (!fm->addr) {
+               rc = -ENODEV;
                goto err_out_free;
+       }
 
        rc = request_irq(dev->irq, tifm_7xx1_isr, IRQF_SHARED, DRIVER_NAME, fm);
        if (rc)
@@ -378,7 +380,6 @@ err_out_irq:
 err_out_unmap:
        iounmap(fm->addr);
 err_out_free:
-       pci_set_drvdata(dev, NULL);
        tifm_free_adapter(fm);
 err_out_int:
        pci_intx(dev, 0);
@@ -405,8 +406,6 @@ static void tifm_7xx1_remove(struct pci_dev *dev)
        for (cnt = 0; cnt < fm->num_sockets; cnt++)
                tifm_7xx1_sock_power_off(tifm_7xx1_sock_addr(fm->addr, cnt));
 
-       pci_set_drvdata(dev, NULL);
-
        iounmap(fm->addr);
        pci_intx(dev, 0);
        pci_release_regions(dev);
index 0ab7c922212cd860703d78a85621bfe68a067825..a511b2a713b33b9b6dce502c6cbb46cc930b8d58 100644 (file)
@@ -145,15 +145,17 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr,
        struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
        return sprintf(buf, "%x", sock->type);
 }
+static DEVICE_ATTR_RO(type);
 
-static struct device_attribute tifm_dev_attrs[] = {
-       __ATTR(type, S_IRUGO, type_show, NULL),
-       __ATTR_NULL
+static struct attribute *tifm_dev_attrs[] = {
+       &dev_attr_type.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(tifm_dev);
 
 static struct bus_type tifm_bus_type = {
        .name      = "tifm",
-       .dev_attrs = tifm_dev_attrs,
+       .dev_groups = tifm_dev_groups,
        .match     = tifm_bus_match,
        .uevent    = tifm_uevent,
        .probe     = tifm_device_probe,
index b3a2b763ecf25bd792b762c8f30778012398320a..c98b03b993537aa62ff1e38242b6ad9dbc59b96d 100644 (file)
@@ -649,7 +649,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
        return 0;
 
 err_free_irq:
-       free_irq(vmci_dev->irq, &vmci_dev);
+       free_irq(vmci_dev->irq, vmci_dev);
        tasklet_kill(&vmci_dev->datagram_tasklet);
        tasklet_kill(&vmci_dev->bm_tasklet);
 
index d4722b3dc8ec762987f18d25268727ea9954b32c..1723a6e4f2e8ea97b70e3d151f0bc0e29dc29c15 100644 (file)
@@ -243,11 +243,7 @@ static int vmci_host_setup_notify(struct vmci_ctx *context,
        /*
         * Lock physical page backing a given user VA.
         */
-       down_read(&current->mm->mmap_sem);
-       retval = get_user_pages(current, current->mm,
-                               PAGE_ALIGN(uva),
-                               1, 1, 0, &page, NULL);
-       up_read(&current->mm->mmap_sem);
+       retval = get_user_pages_fast(PAGE_ALIGN(uva), 1, 1, &page);
        if (retval != 1)
                return VMCI_ERROR_GENERIC;
 
index a0515a6d6ebdbcf1438e4e8c544d879fc478e08a..1b7b303085d28d459c2595776fd82b15cf9a0dc5 100644 (file)
@@ -732,13 +732,9 @@ static int qp_host_get_user_memory(u64 produce_uva,
        int retval;
        int err = VMCI_SUCCESS;
 
-       down_write(&current->mm->mmap_sem);
-       retval = get_user_pages(current,
-                               current->mm,
-                               (uintptr_t) produce_uva,
-                               produce_q->kernel_if->num_pages,
-                               1, 0,
-                               produce_q->kernel_if->u.h.header_page, NULL);
+       retval = get_user_pages_fast((uintptr_t) produce_uva,
+                                    produce_q->kernel_if->num_pages, 1,
+                                    produce_q->kernel_if->u.h.header_page);
        if (retval < produce_q->kernel_if->num_pages) {
                pr_warn("get_user_pages(produce) failed (retval=%d)", retval);
                qp_release_pages(produce_q->kernel_if->u.h.header_page,
@@ -747,12 +743,9 @@ static int qp_host_get_user_memory(u64 produce_uva,
                goto out;
        }
 
-       retval = get_user_pages(current,
-                               current->mm,
-                               (uintptr_t) consume_uva,
-                               consume_q->kernel_if->num_pages,
-                               1, 0,
-                               consume_q->kernel_if->u.h.header_page, NULL);
+       retval = get_user_pages_fast((uintptr_t) consume_uva,
+                                    consume_q->kernel_if->num_pages, 1,
+                                    consume_q->kernel_if->u.h.header_page);
        if (retval < consume_q->kernel_if->num_pages) {
                pr_warn("get_user_pages(consume) failed (retval=%d)", retval);
                qp_release_pages(consume_q->kernel_if->u.h.header_page,
@@ -763,8 +756,6 @@ static int qp_host_get_user_memory(u64 produce_uva,
        }
 
  out:
-       up_write(&current->mm->mmap_sem);
-
        return err;
 }
 
index 704bf66f58733a036bf79bfc4ef585f29acb7626..3e227bd91e81935eaf3f4ffce830de99b3e1321d 100644 (file)
@@ -27,7 +27,7 @@
 
 #define to_mmc_driver(d)       container_of(d, struct mmc_driver, drv)
 
-static ssize_t mmc_type_show(struct device *dev,
+static ssize_t type_show(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
        struct mmc_card *card = mmc_dev_to_card(dev);
@@ -45,11 +45,13 @@ static ssize_t mmc_type_show(struct device *dev,
                return -EFAULT;
        }
 }
+static DEVICE_ATTR_RO(type);
 
-static struct device_attribute mmc_dev_attrs[] = {
-       __ATTR(type, S_IRUGO, mmc_type_show, NULL),
-       __ATTR_NULL,
+static struct attribute *mmc_dev_attrs[] = {
+       &dev_attr_type.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(mmc_dev);
 
 /*
  * This currently matches any MMC driver to any MMC card - drivers
@@ -218,7 +220,7 @@ static const struct dev_pm_ops mmc_bus_pm_ops = {
 
 static struct bus_type mmc_bus_type = {
        .name           = "mmc",
-       .dev_attrs      = mmc_dev_attrs,
+       .dev_groups     = mmc_dev_groups,
        .match          = mmc_bus_match,
        .uevent         = mmc_bus_uevent,
        .probe          = mmc_bus_probe,
index 6d67492a9247b14f0ed07c2c2f9373bf0be7cdcd..ef8956568c3a2978b90cab02a9dc0ed56c76254f 100644 (file)
@@ -34,7 +34,8 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf)                            \
                                                                        \
        func = dev_to_sdio_func (dev);                                  \
        return sprintf (buf, format_string, func->field);               \
-}
+}                                                                      \
+static DEVICE_ATTR_RO(field)
 
 sdio_config_attr(class, "0x%02x\n");
 sdio_config_attr(vendor, "0x%04x\n");
@@ -47,14 +48,16 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
        return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
                        func->class, func->vendor, func->device);
 }
-
-static struct device_attribute sdio_dev_attrs[] = {
-       __ATTR_RO(class),
-       __ATTR_RO(vendor),
-       __ATTR_RO(device),
-       __ATTR_RO(modalias),
-       __ATTR_NULL,
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *sdio_dev_attrs[] = {
+       &dev_attr_class.attr,
+       &dev_attr_vendor.attr,
+       &dev_attr_device.attr,
+       &dev_attr_modalias.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(sdio_dev);
 
 static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
        const struct sdio_device_id *id)
@@ -225,7 +228,7 @@ static const struct dev_pm_ops sdio_bus_pm_ops = {
 
 static struct bus_type sdio_bus_type = {
        .name           = "sdio",
-       .dev_attrs      = sdio_dev_attrs,
+       .dev_groups     = sdio_dev_groups,
        .match          = sdio_bus_match,
        .uevent         = sdio_bus_uevent,
        .probe          = sdio_bus_probe,
index 06c5b0b28ebc99f547b88fb9be47a43196872ff8..deecee08c2881d0ee6ad34a9d049368e5d05ceec 100644 (file)
@@ -655,7 +655,7 @@ static const struct mmc_host_ops mvsd_ops = {
        .enable_sdio_irq        = mvsd_enable_sdio_irq,
 };
 
-static void __init
+static void
 mv_conf_mbus_windows(struct mvsd_host *host,
                     const struct mbus_dram_target_info *dram)
 {
@@ -677,7 +677,7 @@ mv_conf_mbus_windows(struct mvsd_host *host,
        }
 }
 
-static int __init mvsd_probe(struct platform_device *pdev)
+static int mvsd_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
        struct mmc_host *mmc = NULL;
@@ -819,7 +819,7 @@ out:
        return ret;
 }
 
-static int __exit mvsd_remove(struct platform_device *pdev)
+static int mvsd_remove(struct platform_device *pdev)
 {
        struct mmc_host *mmc = platform_get_drvdata(pdev);
 
@@ -872,7 +872,8 @@ static const struct of_device_id mvsdio_dt_ids[] = {
 MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);
 
 static struct platform_driver mvsd_driver = {
-       .remove         = __exit_p(mvsd_remove),
+       .probe          = mvsd_probe,
+       .remove         = mvsd_remove,
        .suspend        = mvsd_suspend,
        .resume         = mvsd_resume,
        .driver         = {
@@ -881,7 +882,7 @@ static struct platform_driver mvsd_driver = {
        },
 };
 
-module_platform_driver_probe(mvsd_driver, mvsd_probe);
+module_platform_driver(mvsd_driver);
 
 /* maximum card clock frequency (default 50MHz) */
 module_param(maxfreq, int, 0);
index 060feeaf6b3e5554328904a1ac870a97cbc5685b..bd1ce7d137023fed1f42bc606166a111946466c1 100644 (file)
@@ -1139,7 +1139,7 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host,
        return 0;
 }
 
-static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev,
+static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
                                         struct atmel_nand_host *host)
 {
        struct mtd_info *mtd = &host->mtd;
@@ -1548,7 +1548,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
 }
 #endif
 
-static int __init atmel_hw_nand_init_params(struct platform_device *pdev,
+static int atmel_hw_nand_init_params(struct platform_device *pdev,
                                         struct atmel_nand_host *host)
 {
        struct mtd_info *mtd = &host->mtd;
@@ -1987,7 +1987,7 @@ static struct platform_driver atmel_nand_nfc_driver;
 /*
  * Probe for the NAND device.
  */
-static int __init atmel_nand_probe(struct platform_device *pdev)
+static int atmel_nand_probe(struct platform_device *pdev)
 {
        struct atmel_nand_host *host;
        struct mtd_info *mtd;
@@ -2184,7 +2184,7 @@ err_nand_ioremap:
 /*
  * Remove a NAND device.
  */
-static int __exit atmel_nand_remove(struct platform_device *pdev)
+static int atmel_nand_remove(struct platform_device *pdev)
 {
        struct atmel_nand_host *host = platform_get_drvdata(pdev);
        struct mtd_info *mtd = &host->mtd;
@@ -2270,7 +2270,8 @@ static struct platform_driver atmel_nand_nfc_driver = {
 };
 
 static struct platform_driver atmel_nand_driver = {
-       .remove         = __exit_p(atmel_nand_remove),
+       .probe          = atmel_nand_probe,
+       .remove         = atmel_nand_remove,
        .driver         = {
                .name   = "atmel_nand",
                .owner  = THIS_MODULE,
@@ -2278,7 +2279,7 @@ static struct platform_driver atmel_nand_driver = {
        },
 };
 
-module_platform_driver_probe(atmel_nand_driver, atmel_nand_probe);
+module_platform_driver(atmel_nand_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Rick Bronson");
index 59ab0692f0b97f58fef750b6f5e29c11b6fa0367..a9830ff8e3f38443a69d2e1d599c8dc45e136ff1 100644 (file)
@@ -349,7 +349,7 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
 
 int common_nfc_set_geometry(struct gpmi_nand_data *this)
 {
-       return set_geometry_by_ecc_info(this) ? 0 : legacy_set_geometry(this);
+       return legacy_set_geometry(this);
 }
 
 struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
index dd03dfdfb0d65e0ded1b0a124221663b586482b5..c28d4e29af1a549dfd0c59fd20870da3a7a676f9 100644 (file)
@@ -1320,7 +1320,12 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
        for (cs = 0; cs < pdata->num_cs; cs++) {
                struct mtd_info *mtd = info->host[cs]->mtd;
 
-               mtd->name = pdev->name;
+               /*
+                * The mtd name matches the one used in 'mtdparts' kernel
+                * parameter. This name cannot be changed or otherwise
+                * user's mtd partitions configuration would get broken.
+                */
+               mtd->name = "pxa3xx_nand-0";
                info->cs = cs;
                ret = pxa3xx_nand_scan(mtd);
                if (ret) {
index c29b836749b6323fe86c35e7762b25a3c8596978..ec9b6460a38dd0549b5b69f281b36f6cce0a3e7d 100644 (file)
@@ -149,14 +149,6 @@ err_no_cmd:
        return -EPERM;
 }
 
-static const void *bonding_namespace(struct class *cls,
-                                    const struct class_attribute *attr)
-{
-       const struct bond_net *bn =
-               container_of(attr, struct bond_net, class_attr_bonding_masters);
-       return bn->net;
-}
-
 /* class attribute for bond_masters file.  This ends up in /sys/class/net */
 static const struct class_attribute class_attr_bonding_masters = {
        .attr = {
@@ -165,7 +157,6 @@ static const struct class_attribute class_attr_bonding_masters = {
        },
        .show = bonding_show_bonds,
        .store = bonding_store_bonds,
-       .namespace = bonding_namespace,
 };
 
 int bond_create_slave_symlinks(struct net_device *master,
@@ -1787,7 +1778,8 @@ int bond_create_sysfs(struct bond_net *bn)
        bn->class_attr_bonding_masters = class_attr_bonding_masters;
        sysfs_attr_init(&bn->class_attr_bonding_masters.attr);
 
-       ret = netdev_class_create_file(&bn->class_attr_bonding_masters);
+       ret = netdev_class_create_file_ns(&bn->class_attr_bonding_masters,
+                                         bn->net);
        /*
         * Permit multiple loads of the module by ignoring failures to
         * create the bonding_masters sysfs file.  Bonding devices
@@ -1817,7 +1809,7 @@ int bond_create_sysfs(struct bond_net *bn)
  */
 void bond_destroy_sysfs(struct bond_net *bn)
 {
-       netdev_class_remove_file(&bn->class_attr_bonding_masters);
+       netdev_class_remove_file_ns(&bn->class_attr_bonding_masters, bn->net);
 }
 
 /*
index 3b1ff6148702beb3818fbae7da2b0206f7d1c7f4..693d8ffe465311b6aab964c25304f4bbdd04cdf0 100644 (file)
@@ -1405,10 +1405,10 @@ static int at91_can_remove(struct platform_device *pdev)
 
 static const struct platform_device_id at91_can_id_table[] = {
        {
-               .name = "at91_can",
+               .name = "at91sam9x5_can",
                .driver_data = (kernel_ulong_t)&at91_at91sam9x5_data,
        }, {
-               .name = "at91sam9x5_can",
+               .name = "at91_can",
                .driver_data = (kernel_ulong_t)&at91_at91sam9263_data,
        }, {
                /* sentinel */
index a668cd491cb3ae427e38db535df7a8c9dc1109e1..e3fc07cf2f6269e5ccf0ca5dbf42897c191cecd4 100644 (file)
@@ -814,9 +814,6 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
                        msg_ctrl_save = priv->read_reg(priv,
                                        C_CAN_IFACE(MSGCTRL_REG, 0));
 
-                       if (msg_ctrl_save & IF_MCONT_EOB)
-                               return num_rx_pkts;
-
                        if (msg_ctrl_save & IF_MCONT_MSGLST) {
                                c_can_handle_lost_msg_obj(dev, 0, msg_obj);
                                num_rx_pkts++;
@@ -824,6 +821,9 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
                                continue;
                        }
 
+                       if (msg_ctrl_save & IF_MCONT_EOB)
+                               return num_rx_pkts;
+
                        if (!(msg_ctrl_save & IF_MCONT_NEWDAT))
                                continue;
 
index f9cba4123c663084b66c2dbdaac5885a579278fd..1870c4731a572d193bef5e6dc0b7364e5f1751ed 100644 (file)
@@ -705,14 +705,14 @@ static size_t can_get_size(const struct net_device *dev)
        size_t size;
 
        size = nla_total_size(sizeof(u32));   /* IFLA_CAN_STATE */
-       size += sizeof(struct can_ctrlmode);  /* IFLA_CAN_CTRLMODE */
+       size += nla_total_size(sizeof(struct can_ctrlmode));  /* IFLA_CAN_CTRLMODE */
        size += nla_total_size(sizeof(u32));  /* IFLA_CAN_RESTART_MS */
-       size += sizeof(struct can_bittiming); /* IFLA_CAN_BITTIMING */
-       size += sizeof(struct can_clock);     /* IFLA_CAN_CLOCK */
+       size += nla_total_size(sizeof(struct can_bittiming)); /* IFLA_CAN_BITTIMING */
+       size += nla_total_size(sizeof(struct can_clock));     /* IFLA_CAN_CLOCK */
        if (priv->do_get_berr_counter)        /* IFLA_CAN_BERR_COUNTER */
-               size += sizeof(struct can_berr_counter);
+               size += nla_total_size(sizeof(struct can_berr_counter));
        if (priv->bittiming_const)            /* IFLA_CAN_BITTIMING_CONST */
-               size += sizeof(struct can_bittiming_const);
+               size += nla_total_size(sizeof(struct can_bittiming_const));
 
        return size;
 }
index 3f21142138b79868a0f18fa7d246432e99d96192..8f5ce747feb57a093dc78e4a24bd9742b5ea99fc 100644 (file)
@@ -62,7 +62,7 @@
 #define FLEXCAN_MCR_BCC                        BIT(16)
 #define FLEXCAN_MCR_LPRIO_EN           BIT(13)
 #define FLEXCAN_MCR_AEN                        BIT(12)
-#define FLEXCAN_MCR_MAXMB(x)           ((x) & 0xf)
+#define FLEXCAN_MCR_MAXMB(x)           ((x) & 0x1f)
 #define FLEXCAN_MCR_IDAM_A             (0 << 8)
 #define FLEXCAN_MCR_IDAM_B             (1 << 8)
 #define FLEXCAN_MCR_IDAM_C             (2 << 8)
@@ -735,9 +735,11 @@ static int flexcan_chip_start(struct net_device *dev)
         *
         */
        reg_mcr = flexcan_read(&regs->mcr);
+       reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
        reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
                FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
-               FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS;
+               FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS |
+               FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
        netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
        flexcan_write(reg_mcr, &regs->mcr);
 
@@ -771,6 +773,10 @@ static int flexcan_chip_start(struct net_device *dev)
        netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
        flexcan_write(reg_ctrl, &regs->ctrl);
 
+       /* Abort any pending TX, mark Mailbox as INACTIVE */
+       flexcan_write(FLEXCAN_MB_CNT_CODE(0x4),
+                     &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+
        /* acceptance mask/acceptance code (accept everything) */
        flexcan_write(0x0, &regs->rxgmask);
        flexcan_write(0x0, &regs->rx14mask);
@@ -979,9 +985,9 @@ static void unregister_flexcandev(struct net_device *dev)
 }
 
 static const struct of_device_id flexcan_of_match[] = {
-       { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
-       { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
        { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
+       { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
+       { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, flexcan_of_match);
index 3b95465882401fc556c647071b8c27f5724a8dbf..4b2d5ed62b119197ef5f25f23412247878cba36a 100644 (file)
@@ -1544,9 +1544,9 @@ static int kvaser_usb_init_one(struct usb_interface *intf,
        return 0;
 }
 
-static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
-                                    struct usb_endpoint_descriptor **in,
-                                    struct usb_endpoint_descriptor **out)
+static int kvaser_usb_get_endpoints(const struct usb_interface *intf,
+                                   struct usb_endpoint_descriptor **in,
+                                   struct usb_endpoint_descriptor **out)
 {
        const struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
@@ -1557,12 +1557,18 @@ static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
 
-               if (usb_endpoint_is_bulk_in(endpoint))
+               if (!*in && usb_endpoint_is_bulk_in(endpoint))
                        *in = endpoint;
 
-               if (usb_endpoint_is_bulk_out(endpoint))
+               if (!*out && usb_endpoint_is_bulk_out(endpoint))
                        *out = endpoint;
+
+               /* use first bulk endpoint for in and out */
+               if (*in && *out)
+                       return 0;
        }
+
+       return -ENODEV;
 }
 
 static int kvaser_usb_probe(struct usb_interface *intf,
@@ -1576,8 +1582,8 @@ static int kvaser_usb_probe(struct usb_interface *intf,
        if (!dev)
                return -ENOMEM;
 
-       kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
-       if (!dev->bulk_in || !dev->bulk_out) {
+       err = kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+       if (err) {
                dev_err(&intf->dev, "Cannot get usb endpoint(s)");
                return err;
        }
index 249468f953651480a5e0b897d582dd09743c6c3b..9e8a3e024e0150e71b77057c83c6aa3bdf1aabcf 100644 (file)
@@ -244,25 +244,33 @@ static int bgmac_dma_rx_skb_for_slot(struct bgmac *bgmac,
                                     struct bgmac_slot_info *slot)
 {
        struct device *dma_dev = bgmac->core->dma_dev;
+       struct sk_buff *skb;
+       dma_addr_t dma_addr;
        struct bgmac_rx_header *rx;
 
        /* Alloc skb */
-       slot->skb = netdev_alloc_skb(bgmac->net_dev, BGMAC_RX_BUF_SIZE);
-       if (!slot->skb)
+       skb = netdev_alloc_skb(bgmac->net_dev, BGMAC_RX_BUF_SIZE);
+       if (!skb)
                return -ENOMEM;
 
        /* Poison - if everything goes fine, hardware will overwrite it */
-       rx = (struct bgmac_rx_header *)slot->skb->data;
+       rx = (struct bgmac_rx_header *)skb->data;
        rx->len = cpu_to_le16(0xdead);
        rx->flags = cpu_to_le16(0xbeef);
 
        /* Map skb for the DMA */
-       slot->dma_addr = dma_map_single(dma_dev, slot->skb->data,
-                                       BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
-       if (dma_mapping_error(dma_dev, slot->dma_addr)) {
+       dma_addr = dma_map_single(dma_dev, skb->data,
+                                 BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(dma_dev, dma_addr)) {
                bgmac_err(bgmac, "DMA mapping error\n");
+               dev_kfree_skb(skb);
                return -ENOMEM;
        }
+
+       /* Update the slot */
+       slot->skb = skb;
+       slot->dma_addr = dma_addr;
+
        if (slot->dma_addr & 0xC0000000)
                bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n");
 
index 97b3d32a98bd010ab1a1327ef7382e5bd64139b8..c5e375ddd6c09c137232ecfc8264439bc614e08e 100644 (file)
@@ -1197,8 +1197,9 @@ union cdu_context {
 /* TM (timers) host DB constants */
 #define TM_ILT_PAGE_SZ_HW      0
 #define TM_ILT_PAGE_SZ         (4096 << TM_ILT_PAGE_SZ_HW) /* 4K */
-/* #define TM_CONN_NUM         (CNIC_STARTING_CID+CNIC_ISCSI_CXT_MAX) */
-#define TM_CONN_NUM            1024
+#define TM_CONN_NUM            (BNX2X_FIRST_VF_CID + \
+                                BNX2X_VF_CIDS + \
+                                CNIC_ISCSI_CID_MAX)
 #define TM_ILT_SZ              (8 * TM_CONN_NUM)
 #define TM_ILT_LINES           DIV_ROUND_UP(TM_ILT_SZ, TM_ILT_PAGE_SZ)
 
@@ -1527,7 +1528,6 @@ struct bnx2x {
 #define PCI_32BIT_FLAG                 (1 << 1)
 #define ONE_PORT_FLAG                  (1 << 2)
 #define NO_WOL_FLAG                    (1 << 3)
-#define USING_DAC_FLAG                 (1 << 4)
 #define USING_MSIX_FLAG                        (1 << 5)
 #define USING_MSI_FLAG                 (1 << 6)
 #define DISABLE_MSI_FLAG               (1 << 7)
@@ -1621,7 +1621,7 @@ struct bnx2x {
        u16                     rx_ticks_int;
        u16                     rx_ticks;
 /* Maximal coalescing timeout in us */
-#define BNX2X_MAX_COALESCE_TOUT                (0xf0*12)
+#define BNX2X_MAX_COALESCE_TOUT                (0xff*BNX2X_BTR)
 
        u32                     lin_cnt;
 
@@ -2072,7 +2072,8 @@ u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type,
 
 void bnx2x_prep_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae,
                               u8 src_type, u8 dst_type);
-int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae);
+int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae,
+                              u32 *comp);
 
 /* FLR related routines */
 u32 bnx2x_flr_clnup_poll_count(struct bnx2x *bp);
@@ -2498,4 +2499,8 @@ enum bnx2x_pci_bus_speed {
 };
 
 void bnx2x_set_local_cmng(struct bnx2x *bp);
+
+#define MCPR_SCRATCH_BASE(bp) \
+       (CHIP_IS_E1x(bp) ? MCP_REG_MCPR_SCRATCH : MCP_A_REG_MCPR_SCRATCH)
+
 #endif /* bnx2x.h */
index e66beff2704d19241695fe5028e33efb3ca95519..74d6486fccfde2cba3128410a9d8b59c79a98918 100644 (file)
@@ -681,6 +681,7 @@ static void bnx2x_gro_receive(struct bnx2x *bp, struct bnx2x_fastpath *fp,
                }
        }
 #endif
+       skb_record_rx_queue(skb, fp->rx_queue);
        napi_gro_receive(&fp->napi, skb);
 }
 
@@ -2544,10 +2545,6 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
                }
        }
 
-       /* Allocated memory for FW statistics  */
-       if (bnx2x_alloc_fw_stats_mem(bp))
-               LOAD_ERROR_EXIT(bp, load_error0);
-
        /* need to be done after alloc mem, since it's self adjusting to amount
         * of memory available for RSS queues
         */
@@ -2557,6 +2554,10 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
                LOAD_ERROR_EXIT(bp, load_error0);
        }
 
+       /* Allocated memory for FW statistics  */
+       if (bnx2x_alloc_fw_stats_mem(bp))
+               LOAD_ERROR_EXIT(bp, load_error0);
+
        /* request pf to initialize status blocks */
        if (IS_VF(bp)) {
                rc = bnx2x_vfpf_init(bp);
@@ -2811,8 +2812,8 @@ load_error1:
        if (IS_PF(bp))
                bnx2x_clear_pf_load(bp);
 load_error0:
-       bnx2x_free_fp_mem(bp);
        bnx2x_free_fw_stats_mem(bp);
+       bnx2x_free_fp_mem(bp);
        bnx2x_free_mem(bp);
 
        return rc;
index 324de5f05332e78aa6c108d23891105880ee5bd8..e8efa1c93ffecdefde5183e91cbf67d7286928af 100644 (file)
@@ -891,17 +891,8 @@ static void bnx2x_get_regs(struct net_device *dev,
         * will re-enable parity attentions right after the dump.
         */
 
-       /* Disable parity on path 0 */
-       bnx2x_pretend_func(bp, 0);
        bnx2x_disable_blocks_parity(bp);
 
-       /* Disable parity on path 1 */
-       bnx2x_pretend_func(bp, 1);
-       bnx2x_disable_blocks_parity(bp);
-
-       /* Return to current function */
-       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
-
        dump_hdr.header_size = (sizeof(struct dump_header) / 4) - 1;
        dump_hdr.preset = DUMP_ALL_PRESETS;
        dump_hdr.version = BNX2X_DUMP_VERSION;
@@ -928,18 +919,9 @@ static void bnx2x_get_regs(struct net_device *dev,
        /* Actually read the registers */
        __bnx2x_get_regs(bp, p);
 
-       /* Re-enable parity attentions on path 0 */
-       bnx2x_pretend_func(bp, 0);
+       /* Re-enable parity attentions */
        bnx2x_clear_blocks_parity(bp);
        bnx2x_enable_blocks_parity(bp);
-
-       /* Re-enable parity attentions on path 1 */
-       bnx2x_pretend_func(bp, 1);
-       bnx2x_clear_blocks_parity(bp);
-       bnx2x_enable_blocks_parity(bp);
-
-       /* Return to current function */
-       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
 }
 
 static int bnx2x_get_preset_regs_len(struct net_device *dev, u32 preset)
@@ -993,17 +975,8 @@ static int bnx2x_get_dump_data(struct net_device *dev,
         * will re-enable parity attentions right after the dump.
         */
 
-       /* Disable parity on path 0 */
-       bnx2x_pretend_func(bp, 0);
        bnx2x_disable_blocks_parity(bp);
 
-       /* Disable parity on path 1 */
-       bnx2x_pretend_func(bp, 1);
-       bnx2x_disable_blocks_parity(bp);
-
-       /* Return to current function */
-       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
-
        dump_hdr.header_size = (sizeof(struct dump_header) / 4) - 1;
        dump_hdr.preset = bp->dump_preset_idx;
        dump_hdr.version = BNX2X_DUMP_VERSION;
@@ -1032,19 +1005,10 @@ static int bnx2x_get_dump_data(struct net_device *dev,
        /* Actually read the registers */
        __bnx2x_get_preset_regs(bp, p, dump_hdr.preset);
 
-       /* Re-enable parity attentions on path 0 */
-       bnx2x_pretend_func(bp, 0);
+       /* Re-enable parity attentions */
        bnx2x_clear_blocks_parity(bp);
        bnx2x_enable_blocks_parity(bp);
 
-       /* Re-enable parity attentions on path 1 */
-       bnx2x_pretend_func(bp, 1);
-       bnx2x_clear_blocks_parity(bp);
-       bnx2x_enable_blocks_parity(bp);
-
-       /* Return to current function */
-       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
-
        return 0;
 }
 
index 76df015f486ad9d1653efccde3538dbb47263849..c2dfea7968f452defdca6fd4b1ea68758381038e 100644 (file)
@@ -640,23 +640,35 @@ static const struct {
  * [30] MCP Latched ump_tx_parity
  * [31] MCP Latched scpad_parity
  */
-#define MISC_AEU_ENABLE_MCP_PRTY_BITS  \
+#define MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS      \
        (AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY | \
         AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY | \
-        AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY | \
+        AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY)
+
+#define MISC_AEU_ENABLE_MCP_PRTY_BITS  \
+       (MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS | \
         AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY)
 
 /* Below registers control the MCP parity attention output. When
  * MISC_AEU_ENABLE_MCP_PRTY_BITS are set - attentions are
  * enabled, when cleared - disabled.
  */
-static const u32 mcp_attn_ctl_regs[] = {
-       MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0,
-       MISC_REG_AEU_ENABLE4_NIG_0,
-       MISC_REG_AEU_ENABLE4_PXP_0,
-       MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0,
-       MISC_REG_AEU_ENABLE4_NIG_1,
-       MISC_REG_AEU_ENABLE4_PXP_1
+static const struct {
+       u32 addr;
+       u32 bits;
+} mcp_attn_ctl_regs[] = {
+       { MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0,
+               MISC_AEU_ENABLE_MCP_PRTY_BITS },
+       { MISC_REG_AEU_ENABLE4_NIG_0,
+               MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS },
+       { MISC_REG_AEU_ENABLE4_PXP_0,
+               MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS },
+       { MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0,
+               MISC_AEU_ENABLE_MCP_PRTY_BITS },
+       { MISC_REG_AEU_ENABLE4_NIG_1,
+               MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS },
+       { MISC_REG_AEU_ENABLE4_PXP_1,
+               MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS }
 };
 
 static inline void bnx2x_set_mcp_parity(struct bnx2x *bp, u8 enable)
@@ -665,14 +677,14 @@ static inline void bnx2x_set_mcp_parity(struct bnx2x *bp, u8 enable)
        u32 reg_val;
 
        for (i = 0; i < ARRAY_SIZE(mcp_attn_ctl_regs); i++) {
-               reg_val = REG_RD(bp, mcp_attn_ctl_regs[i]);
+               reg_val = REG_RD(bp, mcp_attn_ctl_regs[i].addr);
 
                if (enable)
-                       reg_val |= MISC_AEU_ENABLE_MCP_PRTY_BITS;
+                       reg_val |= mcp_attn_ctl_regs[i].bits;
                else
-                       reg_val &= ~MISC_AEU_ENABLE_MCP_PRTY_BITS;
+                       reg_val &= ~mcp_attn_ctl_regs[i].bits;
 
-               REG_WR(bp, mcp_attn_ctl_regs[i], reg_val);
+               REG_WR(bp, mcp_attn_ctl_regs[i].addr, reg_val);
        }
 }
 
index 82b658d8c04c47c52931b5138375cd38f0ca519a..b42f89ce02ef5997fa46e57a9521b0873968d7b7 100644 (file)
@@ -503,9 +503,9 @@ void bnx2x_prep_dmae_with_comp(struct bnx2x *bp,
 }
 
 /* issue a dmae command over the init-channel and wait for completion */
-int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae)
+int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae,
+                              u32 *comp)
 {
-       u32 *wb_comp = bnx2x_sp(bp, wb_comp);
        int cnt = CHIP_REV_IS_SLOW(bp) ? (400000) : 4000;
        int rc = 0;
 
@@ -518,14 +518,14 @@ int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae)
        spin_lock_bh(&bp->dmae_lock);
 
        /* reset completion */
-       *wb_comp = 0;
+       *comp = 0;
 
        /* post the command on the channel used for initializations */
        bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp));
 
        /* wait for completion */
        udelay(5);
-       while ((*wb_comp & ~DMAE_PCI_ERR_FLAG) != DMAE_COMP_VAL) {
+       while ((*comp & ~DMAE_PCI_ERR_FLAG) != DMAE_COMP_VAL) {
 
                if (!cnt ||
                    (bp->recovery_state != BNX2X_RECOVERY_DONE &&
@@ -537,7 +537,7 @@ int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae)
                cnt--;
                udelay(50);
        }
-       if (*wb_comp & DMAE_PCI_ERR_FLAG) {
+       if (*comp & DMAE_PCI_ERR_FLAG) {
                BNX2X_ERR("DMAE PCI error!\n");
                rc = DMAE_PCI_ERROR;
        }
@@ -574,7 +574,7 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
        dmae.len = len32;
 
        /* issue the command and wait for completion */
-       rc = bnx2x_issue_dmae_with_comp(bp, &dmae);
+       rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
        if (rc) {
                BNX2X_ERR("DMAE returned failure %d\n", rc);
                bnx2x_panic();
@@ -611,7 +611,7 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32)
        dmae.len = len32;
 
        /* issue the command and wait for completion */
-       rc = bnx2x_issue_dmae_with_comp(bp, &dmae);
+       rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
        if (rc) {
                BNX2X_ERR("DMAE returned failure %d\n", rc);
                bnx2x_panic();
@@ -751,6 +751,10 @@ static int bnx2x_mc_assert(struct bnx2x *bp)
        return rc;
 }
 
+#define MCPR_TRACE_BUFFER_SIZE (0x800)
+#define SCRATCH_BUFFER_SIZE(bp)        \
+       (CHIP_IS_E1(bp) ? 0x10000 : (CHIP_IS_E1H(bp) ? 0x20000 : 0x28000))
+
 void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl)
 {
        u32 addr, val;
@@ -775,7 +779,17 @@ void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl)
                trace_shmem_base = bp->common.shmem_base;
        else
                trace_shmem_base = SHMEM2_RD(bp, other_shmem_base_addr);
-       addr = trace_shmem_base - 0x800;
+
+       /* sanity */
+       if (trace_shmem_base < MCPR_SCRATCH_BASE(bp) + MCPR_TRACE_BUFFER_SIZE ||
+           trace_shmem_base >= MCPR_SCRATCH_BASE(bp) +
+                               SCRATCH_BUFFER_SIZE(bp)) {
+               BNX2X_ERR("Unable to dump trace buffer (mark %x)\n",
+                         trace_shmem_base);
+               return;
+       }
+
+       addr = trace_shmem_base - MCPR_TRACE_BUFFER_SIZE;
 
        /* validate TRCB signature */
        mark = REG_RD(bp, addr);
@@ -787,14 +801,17 @@ void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl)
        /* read cyclic buffer pointer */
        addr += 4;
        mark = REG_RD(bp, addr);
-       mark = (CHIP_IS_E1x(bp) ? MCP_REG_MCPR_SCRATCH : MCP_A_REG_MCPR_SCRATCH)
-                       + ((mark + 0x3) & ~0x3) - 0x08000000;
+       mark = MCPR_SCRATCH_BASE(bp) + ((mark + 0x3) & ~0x3) - 0x08000000;
+       if (mark >= trace_shmem_base || mark < addr + 4) {
+               BNX2X_ERR("Mark doesn't fall inside Trace Buffer\n");
+               return;
+       }
        printk("%s" "begin fw dump (mark 0x%x)\n", lvl, mark);
 
        printk("%s", lvl);
 
        /* dump buffer after the mark */
-       for (offset = mark; offset <= trace_shmem_base; offset += 0x8*4) {
+       for (offset = mark; offset < trace_shmem_base; offset += 0x8*4) {
                for (word = 0; word < 8; word++)
                        data[word] = htonl(REG_RD(bp, offset + 4*word));
                data[8] = 0x0;
@@ -4280,65 +4297,60 @@ static void _print_next_block(int idx, const char *blk)
        pr_cont("%s%s", idx ? ", " : "", blk);
 }
 
-static int bnx2x_check_blocks_with_parity0(struct bnx2x *bp, u32 sig,
-                                           int par_num, bool print)
+static bool bnx2x_check_blocks_with_parity0(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       u32 cur_bit;
+       bool res;
+       int i;
+
+       res = false;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
-                       switch (cur_bit) {
-                       case AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "BRB");
+                       res |= true; /* Each bit is real error! */
+
+                       if (print) {
+                               switch (cur_bit) {
+                               case AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "BRB");
                                        _print_parity(bp,
                                                      BRB1_REG_BRB1_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "PARSER");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "PARSER");
                                        _print_parity(bp, PRS_REG_PRS_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "TSDM");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "TSDM");
                                        _print_parity(bp,
                                                      TSDM_REG_TSDM_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++,
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
                                                          "SEARCHER");
                                        _print_parity(bp, SRC_REG_SRC_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "TCM");
-                                       _print_parity(bp,
-                                                     TCM_REG_TCM_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "TSEMI");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "TCM");
+                                       _print_parity(bp, TCM_REG_TCM_PRTY_STS);
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "TSEMI");
                                        _print_parity(bp,
                                                      TSEM_REG_TSEM_PRTY_STS_0);
                                        _print_parity(bp,
                                                      TSEM_REG_TSEM_PRTY_STS_1);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "XPB");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "XPB");
                                        _print_parity(bp, GRCBASE_XPB +
                                                          PB_REG_PB_PRTY_STS);
+                                       break;
                                }
-                               break;
                        }
 
                        /* Clear the bit */
@@ -4346,53 +4358,59 @@ static int bnx2x_check_blocks_with_parity0(struct bnx2x *bp, u32 sig,
                }
        }
 
-       return par_num;
+       return res;
 }
 
-static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
-                                           int par_num, bool *global,
+static bool bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool *global,
                                            bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       u32 cur_bit;
+       bool res;
+       int i;
+
+       res = false;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
+                       res |= true; /* Each bit is real error! */
                        switch (cur_bit) {
                        case AEU_INPUTS_ATTN_BITS_PBF_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "PBF");
+                                       _print_next_block((*par_num)++, "PBF");
                                        _print_parity(bp, PBF_REG_PBF_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "QM");
+                                       _print_next_block((*par_num)++, "QM");
                                        _print_parity(bp, QM_REG_QM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_TIMERS_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "TM");
+                                       _print_next_block((*par_num)++, "TM");
                                        _print_parity(bp, TM_REG_TM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "XSDM");
+                                       _print_next_block((*par_num)++, "XSDM");
                                        _print_parity(bp,
                                                      XSDM_REG_XSDM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XCM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "XCM");
+                                       _print_next_block((*par_num)++, "XCM");
                                        _print_parity(bp, XCM_REG_XCM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "XSEMI");
+                                       _print_next_block((*par_num)++,
+                                                         "XSEMI");
                                        _print_parity(bp,
                                                      XSEM_REG_XSEM_PRTY_STS_0);
                                        _print_parity(bp,
@@ -4401,7 +4419,7 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                                break;
                        case AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "DOORBELLQ");
                                        _print_parity(bp,
                                                      DORQ_REG_DORQ_PRTY_STS);
@@ -4409,7 +4427,7 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                                break;
                        case AEU_INPUTS_ATTN_BITS_NIG_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "NIG");
+                                       _print_next_block((*par_num)++, "NIG");
                                        if (CHIP_IS_E1x(bp)) {
                                                _print_parity(bp,
                                                        NIG_REG_NIG_PRTY_STS);
@@ -4423,32 +4441,34 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                                break;
                        case AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR:
                                if (print)
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "VAUX PCI CORE");
                                *global = true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "DEBUG");
+                                       _print_next_block((*par_num)++,
+                                                         "DEBUG");
                                        _print_parity(bp, DBG_REG_DBG_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "USDM");
+                                       _print_next_block((*par_num)++, "USDM");
                                        _print_parity(bp,
                                                      USDM_REG_USDM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_UCM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "UCM");
+                                       _print_next_block((*par_num)++, "UCM");
                                        _print_parity(bp, UCM_REG_UCM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "USEMI");
+                                       _print_next_block((*par_num)++,
+                                                         "USEMI");
                                        _print_parity(bp,
                                                      USEM_REG_USEM_PRTY_STS_0);
                                        _print_parity(bp,
@@ -4457,21 +4477,21 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                                break;
                        case AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "UPB");
+                                       _print_next_block((*par_num)++, "UPB");
                                        _print_parity(bp, GRCBASE_UPB +
                                                          PB_REG_PB_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "CSDM");
+                                       _print_next_block((*par_num)++, "CSDM");
                                        _print_parity(bp,
                                                      CSDM_REG_CSDM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_CCM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "CCM");
+                                       _print_next_block((*par_num)++, "CCM");
                                        _print_parity(bp, CCM_REG_CCM_PRTY_STS);
                                }
                                break;
@@ -4482,80 +4502,73 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                }
        }
 
-       return par_num;
+       return res;
 }
 
-static int bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig,
-                                           int par_num, bool print)
+static bool bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       u32 cur_bit;
+       bool res;
+       int i;
+
+       res = false;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
-                       switch (cur_bit) {
-                       case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "CSEMI");
+                       res |= true; /* Each bit is real error! */
+                       if (print) {
+                               switch (cur_bit) {
+                               case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "CSEMI");
                                        _print_parity(bp,
                                                      CSEM_REG_CSEM_PRTY_STS_0);
                                        _print_parity(bp,
                                                      CSEM_REG_CSEM_PRTY_STS_1);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "PXP");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "PXP");
                                        _print_parity(bp, PXP_REG_PXP_PRTY_STS);
                                        _print_parity(bp,
                                                      PXP2_REG_PXP2_PRTY_STS_0);
                                        _print_parity(bp,
                                                      PXP2_REG_PXP2_PRTY_STS_1);
-                               }
-                               break;
-                       case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR:
-                               if (print)
-                                       _print_next_block(par_num++,
-                                       "PXPPCICLOCKCLIENT");
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "CFC");
+                                       break;
+                               case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "PXPPCICLOCKCLIENT");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "CFC");
                                        _print_parity(bp,
                                                      CFC_REG_CFC_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "CDU");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "CDU");
                                        _print_parity(bp, CDU_REG_CDU_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "DMAE");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "DMAE");
                                        _print_parity(bp,
                                                      DMAE_REG_DMAE_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "IGU");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "IGU");
                                        if (CHIP_IS_E1x(bp))
                                                _print_parity(bp,
                                                        HC_REG_HC_PRTY_STS);
                                        else
                                                _print_parity(bp,
                                                        IGU_REG_IGU_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "MISC");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "MISC");
                                        _print_parity(bp,
                                                      MISC_REG_MISC_PRTY_STS);
+                                       break;
                                }
-                               break;
                        }
 
                        /* Clear the bit */
@@ -4563,40 +4576,49 @@ static int bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig,
                }
        }
 
-       return par_num;
+       return res;
 }
 
-static int bnx2x_check_blocks_with_parity3(u32 sig, int par_num,
-                                          bool *global, bool print)
+static bool bnx2x_check_blocks_with_parity3(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool *global,
+                                           bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       bool res = false;
+       u32 cur_bit;
+       int i;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
                        switch (cur_bit) {
                        case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY:
                                if (print)
-                                       _print_next_block(par_num++, "MCP ROM");
+                                       _print_next_block((*par_num)++,
+                                                         "MCP ROM");
                                *global = true;
+                               res |= true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY:
                                if (print)
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "MCP UMP RX");
                                *global = true;
+                               res |= true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY:
                                if (print)
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "MCP UMP TX");
                                *global = true;
+                               res |= true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY:
                                if (print)
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "MCP SCPAD");
-                               *global = true;
+                               /* clear latched SCPAD PATIRY from MCP */
+                               REG_WR(bp, MISC_REG_AEU_CLR_LATCH_SIGNAL,
+                                      1UL << 10);
                                break;
                        }
 
@@ -4605,45 +4627,50 @@ static int bnx2x_check_blocks_with_parity3(u32 sig, int par_num,
                }
        }
 
-       return par_num;
+       return res;
 }
 
-static int bnx2x_check_blocks_with_parity4(struct bnx2x *bp, u32 sig,
-                                           int par_num, bool print)
+static bool bnx2x_check_blocks_with_parity4(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       u32 cur_bit;
+       bool res;
+       int i;
+
+       res = false;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
-                       switch (cur_bit) {
-                       case AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "PGLUE_B");
+                       res |= true; /* Each bit is real error! */
+                       if (print) {
+                               switch (cur_bit) {
+                               case AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "PGLUE_B");
                                        _print_parity(bp,
-                                               PGLUE_B_REG_PGLUE_B_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "ATC");
+                                                     PGLUE_B_REG_PGLUE_B_PRTY_STS);
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "ATC");
                                        _print_parity(bp,
                                                      ATC_REG_ATC_PRTY_STS);
+                                       break;
                                }
-                               break;
                        }
-
                        /* Clear the bit */
                        sig &= ~cur_bit;
                }
        }
 
-       return par_num;
+       return res;
 }
 
 static bool bnx2x_parity_attn(struct bnx2x *bp, bool *global, bool print,
                              u32 *sig)
 {
+       bool res = false;
+
        if ((sig[0] & HW_PRTY_ASSERT_SET_0) ||
            (sig[1] & HW_PRTY_ASSERT_SET_1) ||
            (sig[2] & HW_PRTY_ASSERT_SET_2) ||
@@ -4660,23 +4687,22 @@ static bool bnx2x_parity_attn(struct bnx2x *bp, bool *global, bool print,
                if (print)
                        netdev_err(bp->dev,
                                   "Parity errors detected in blocks: ");
-               par_num = bnx2x_check_blocks_with_parity0(bp,
-                       sig[0] & HW_PRTY_ASSERT_SET_0, par_num, print);
-               par_num = bnx2x_check_blocks_with_parity1(bp,
-                       sig[1] & HW_PRTY_ASSERT_SET_1, par_num, global, print);
-               par_num = bnx2x_check_blocks_with_parity2(bp,
-                       sig[2] & HW_PRTY_ASSERT_SET_2, par_num, print);
-               par_num = bnx2x_check_blocks_with_parity3(
-                       sig[3] & HW_PRTY_ASSERT_SET_3, par_num, global, print);
-               par_num = bnx2x_check_blocks_with_parity4(bp,
-                       sig[4] & HW_PRTY_ASSERT_SET_4, par_num, print);
+               res |= bnx2x_check_blocks_with_parity0(bp,
+                       sig[0] & HW_PRTY_ASSERT_SET_0, &par_num, print);
+               res |= bnx2x_check_blocks_with_parity1(bp,
+                       sig[1] & HW_PRTY_ASSERT_SET_1, &par_num, global, print);
+               res |= bnx2x_check_blocks_with_parity2(bp,
+                       sig[2] & HW_PRTY_ASSERT_SET_2, &par_num, print);
+               res |= bnx2x_check_blocks_with_parity3(bp,
+                       sig[3] & HW_PRTY_ASSERT_SET_3, &par_num, global, print);
+               res |= bnx2x_check_blocks_with_parity4(bp,
+                       sig[4] & HW_PRTY_ASSERT_SET_4, &par_num, print);
 
                if (print)
                        pr_cont("\n");
+       }
 
-               return true;
-       } else
-               return false;
+       return res;
 }
 
 /**
@@ -7126,7 +7152,7 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
        int port = BP_PORT(bp);
        int init_phase = port ? PHASE_PORT1 : PHASE_PORT0;
        u32 low, high;
-       u32 val;
+       u32 val, reg;
 
        DP(NETIF_MSG_HW, "starting port init  port %d\n", port);
 
@@ -7271,6 +7297,17 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
        val |= CHIP_IS_E1(bp) ? 0 : 0x10;
        REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, val);
 
+       /* SCPAD_PARITY should NOT trigger close the gates */
+       reg = port ? MISC_REG_AEU_ENABLE4_NIG_1 : MISC_REG_AEU_ENABLE4_NIG_0;
+       REG_WR(bp, reg,
+              REG_RD(bp, reg) &
+              ~AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY);
+
+       reg = port ? MISC_REG_AEU_ENABLE4_PXP_1 : MISC_REG_AEU_ENABLE4_PXP_0;
+       REG_WR(bp, reg,
+              REG_RD(bp, reg) &
+              ~AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY);
+
        bnx2x_init_block(bp, BLOCK_NIG, init_phase);
 
        if (!CHIP_IS_E1x(bp)) {
@@ -11685,9 +11722,6 @@ static int bnx2x_init_bp(struct bnx2x *bp)
 static int bnx2x_open(struct net_device *dev)
 {
        struct bnx2x *bp = netdev_priv(dev);
-       bool global = false;
-       int other_engine = BP_PATH(bp) ? 0 : 1;
-       bool other_load_status, load_status;
        int rc;
 
        bp->stats_init = true;
@@ -11703,6 +11737,10 @@ static int bnx2x_open(struct net_device *dev)
         * Parity recovery is only relevant for PF driver.
         */
        if (IS_PF(bp)) {
+               int other_engine = BP_PATH(bp) ? 0 : 1;
+               bool other_load_status, load_status;
+               bool global = false;
+
                other_load_status = bnx2x_get_load_status(bp, other_engine);
                load_status = bnx2x_get_load_status(bp, BP_PATH(bp));
                if (!bnx2x_reset_is_done(bp, BP_PATH(bp)) ||
@@ -12080,7 +12118,6 @@ static int bnx2x_set_coherency_mask(struct bnx2x *bp)
        struct device *dev = &bp->pdev->dev;
 
        if (dma_set_mask(dev, DMA_BIT_MASK(64)) == 0) {
-               bp->flags |= USING_DAC_FLAG;
                if (dma_set_coherent_mask(dev, DMA_BIT_MASK(64)) != 0) {
                        dev_err(dev, "dma_set_coherent_mask failed, aborting\n");
                        return -EIO;
@@ -12248,8 +12285,7 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev,
                NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_HIGHDMA;
 
        dev->features |= dev->hw_features | NETIF_F_HW_VLAN_CTAG_RX;
-       if (bp->flags & USING_DAC_FLAG)
-               dev->features |= NETIF_F_HIGHDMA;
+       dev->features |= NETIF_F_HIGHDMA;
 
        /* Add Loopback capability to the device */
        dev->hw_features |= NETIF_F_LOOPBACK;
@@ -12612,24 +12648,24 @@ static int set_max_cos_est(int chip_id)
                return BNX2X_MULTI_TX_COS_E1X;
        case BCM57712:
        case BCM57712_MF:
-       case BCM57712_VF:
                return BNX2X_MULTI_TX_COS_E2_E3A0;
        case BCM57800:
        case BCM57800_MF:
-       case BCM57800_VF:
        case BCM57810:
        case BCM57810_MF:
        case BCM57840_4_10:
        case BCM57840_2_20:
        case BCM57840_O:
        case BCM57840_MFO:
-       case BCM57810_VF:
        case BCM57840_MF:
-       case BCM57840_VF:
        case BCM57811:
        case BCM57811_MF:
-       case BCM57811_VF:
                return BNX2X_MULTI_TX_COS_E3B0;
+       case BCM57712_VF:
+       case BCM57800_VF:
+       case BCM57810_VF:
+       case BCM57840_VF:
+       case BCM57811_VF:
                return 1;
        default:
                pr_err("Unknown board_type (%d), aborting\n", chip_id);
index 9ad012bdd9151e44ce699b640e77273ad9901796..5e07efb6ec1366bcef6b020d228a9b408e9b2000 100644 (file)
@@ -470,10 +470,10 @@ static int bnx2x_vfop_qdtor_cmd(struct bnx2x *bp,
                                 bnx2x_vfop_qdtor, cmd->done);
                return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_qdtor,
                                             cmd->block);
+       } else {
+               BNX2X_ERR("VF[%d] failed to add a vfop\n", vf->abs_vfid);
+               return -ENOMEM;
        }
-       DP(BNX2X_MSG_IOV, "VF[%d] failed to add a vfop. rc %d\n",
-          vf->abs_vfid, vfop->rc);
-       return -ENOMEM;
 }
 
 static void
@@ -2018,6 +2018,8 @@ failed:
 
 void bnx2x_iov_remove_one(struct bnx2x *bp)
 {
+       int vf_idx;
+
        /* if SRIOV is not enabled there's nothing to do */
        if (!IS_SRIOV(bp))
                return;
@@ -2026,6 +2028,18 @@ void bnx2x_iov_remove_one(struct bnx2x *bp)
        pci_disable_sriov(bp->pdev);
        DP(BNX2X_MSG_IOV, "sriov disabled\n");
 
+       /* disable access to all VFs */
+       for (vf_idx = 0; vf_idx < bp->vfdb->sriov.total; vf_idx++) {
+               bnx2x_pretend_func(bp,
+                                  HW_VF_HANDLE(bp,
+                                               bp->vfdb->sriov.first_vf_in_pf +
+                                               vf_idx));
+               DP(BNX2X_MSG_IOV, "disabling internal access for vf %d\n",
+                  bp->vfdb->sriov.first_vf_in_pf + vf_idx);
+               bnx2x_vf_enable_internal(bp, 0);
+               bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
+       }
+
        /* free vf database */
        __bnx2x_iov_free_vfdb(bp);
 }
@@ -3197,7 +3211,7 @@ int bnx2x_enable_sriov(struct bnx2x *bp)
         * the "acquire" messages to appear on the VF PF channel.
         */
        DP(BNX2X_MSG_IOV, "about to call enable sriov\n");
-       pci_disable_sriov(bp->pdev);
+       bnx2x_disable_sriov(bp);
        rc = pci_enable_sriov(bp->pdev, req_vfs);
        if (rc) {
                BNX2X_ERR("pci_enable_sriov failed with %d\n", rc);
@@ -3390,14 +3404,16 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac)
                rc = bnx2x_del_all_macs(bp, mac_obj, BNX2X_ETH_MAC, true);
                if (rc) {
                        BNX2X_ERR("failed to delete eth macs\n");
-                       return -EINVAL;
+                       rc = -EINVAL;
+                       goto out;
                }
 
                /* remove existing uc list macs */
                rc = bnx2x_del_all_macs(bp, mac_obj, BNX2X_UC_LIST_MAC, true);
                if (rc) {
                        BNX2X_ERR("failed to delete uc_list macs\n");
-                       return -EINVAL;
+                       rc = -EINVAL;
+                       goto out;
                }
 
                /* configure the new mac to device */
@@ -3405,6 +3421,7 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac)
                bnx2x_set_mac_one(bp, (u8 *)&bulletin->mac, mac_obj, true,
                                  BNX2X_ETH_MAC, &ramrod_flags);
 
+out:
                bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_MAC);
        }
 
@@ -3467,7 +3484,8 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
                                          &ramrod_flags);
                if (rc) {
                        BNX2X_ERR("failed to delete vlans\n");
-                       return -EINVAL;
+                       rc = -EINVAL;
+                       goto out;
                }
 
                /* send queue update ramrod to configure default vlan and silent
@@ -3501,7 +3519,8 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
                        rc = bnx2x_config_vlan_mac(bp, &ramrod_param);
                        if (rc) {
                                BNX2X_ERR("failed to configure vlan\n");
-                               return -EINVAL;
+                               rc =  -EINVAL;
+                               goto out;
                        }
 
                        /* configure default vlan to vf queue and set silent
@@ -3519,18 +3538,18 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
                rc = bnx2x_queue_state_change(bp, &q_params);
                if (rc) {
                        BNX2X_ERR("Failed to configure default VLAN\n");
-                       return rc;
+                       goto out;
                }
 
                /* clear the flag indicating that this VF needs its vlan
-                * (will only be set if the HV configured th Vlan before vf was
-                * and we were called because the VF came up later
+                * (will only be set if the HV configured the Vlan before vf was
+                * up and we were called because the VF came up later
                 */
+out:
                vf->cfg_flags &= ~VF_CFG_VLAN;
-
                bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_VLAN);
        }
-       return 0;
+       return rc;
 }
 
 /* crc is the first field in the bulletin board. Compute the crc over the
index 86436c77af036d7884b9cc9525646a44f3ec4acf..3b75070411aab83136ac2c245ba9c65682aefebe 100644 (file)
@@ -196,7 +196,7 @@ static void bnx2x_hw_stats_post(struct bnx2x *bp)
 
        } else if (bp->func_stx) {
                *stats_comp = 0;
-               bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp));
+               bnx2x_issue_dmae_with_comp(bp, dmae, stats_comp);
        }
 }
 
index da16953eb2ec58012f059273abf153c7e81293b9..28757dfacf0da0107576e0c8f7db3222fd5f7a1e 100644 (file)
@@ -980,7 +980,7 @@ static int bnx2x_copy32_vf_dmae(struct bnx2x *bp, u8 from_vf,
        dmae.len = len32;
 
        /* issue the command and wait for completion */
-       return bnx2x_issue_dmae_with_comp(bp, &dmae);
+       return bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
 }
 
 static void bnx2x_vf_mbx_resp(struct bnx2x *bp, struct bnx2x_virtf *vf)
index 78d6d6b970e105bb1de4c157a35af4e861e4f280..48f52882a22b07414b7fed808fe72ccd52650300 100644 (file)
 #define XGMAC_DMA_HW_FEATURE   0x00000f58      /* Enabled Hardware Features */
 
 #define XGMAC_ADDR_AE          0x80000000
-#define XGMAC_MAX_FILTER_ADDR  31
 
 /* PMT Control and Status */
 #define XGMAC_PMT_POINTER_RESET        0x80000000
@@ -384,6 +383,7 @@ struct xgmac_priv {
        struct device *device;
        struct napi_struct napi;
 
+       int max_macs;
        struct xgmac_extra_stats xstats;
 
        spinlock_t stats_lock;
@@ -1291,14 +1291,12 @@ static void xgmac_set_rx_mode(struct net_device *dev)
        netdev_dbg(priv->dev, "# mcasts %d, # unicast %d\n",
                 netdev_mc_count(dev), netdev_uc_count(dev));
 
-       if (dev->flags & IFF_PROMISC) {
-               writel(XGMAC_FRAME_FILTER_PR, ioaddr + XGMAC_FRAME_FILTER);
-               return;
-       }
+       if (dev->flags & IFF_PROMISC)
+               value |= XGMAC_FRAME_FILTER_PR;
 
        memset(hash_filter, 0, sizeof(hash_filter));
 
-       if (netdev_uc_count(dev) > XGMAC_MAX_FILTER_ADDR) {
+       if (netdev_uc_count(dev) > priv->max_macs) {
                use_hash = true;
                value |= XGMAC_FRAME_FILTER_HUC | XGMAC_FRAME_FILTER_HPF;
        }
@@ -1321,7 +1319,7 @@ static void xgmac_set_rx_mode(struct net_device *dev)
                goto out;
        }
 
-       if ((netdev_mc_count(dev) + reg - 1) > XGMAC_MAX_FILTER_ADDR) {
+       if ((netdev_mc_count(dev) + reg - 1) > priv->max_macs) {
                use_hash = true;
                value |= XGMAC_FRAME_FILTER_HMC | XGMAC_FRAME_FILTER_HPF;
        } else {
@@ -1342,8 +1340,8 @@ static void xgmac_set_rx_mode(struct net_device *dev)
        }
 
 out:
-       for (i = reg; i < XGMAC_MAX_FILTER_ADDR; i++)
-               xgmac_set_mac_addr(ioaddr, NULL, reg);
+       for (i = reg; i <= priv->max_macs; i++)
+               xgmac_set_mac_addr(ioaddr, NULL, i);
        for (i = 0; i < XGMAC_NUM_HASH; i++)
                writel(hash_filter[i], ioaddr + XGMAC_HASH(i));
 
@@ -1761,6 +1759,13 @@ static int xgmac_probe(struct platform_device *pdev)
        uid = readl(priv->base + XGMAC_VERSION);
        netdev_info(ndev, "h/w version is 0x%x\n", uid);
 
+       /* Figure out how many valid mac address filter registers we have */
+       writel(1, priv->base + XGMAC_ADDR_HIGH(31));
+       if (readl(priv->base + XGMAC_ADDR_HIGH(31)) == 1)
+               priv->max_macs = 31;
+       else
+               priv->max_macs = 7;
+
        writel(0, priv->base + XGMAC_DMA_INTR_ENA);
        ndev->irq = platform_get_irq(pdev, 0);
        if (ndev->irq == -ENXIO) {
index 9c89dc8fe1057dc1c410728bd252f135f9a80879..632b318eb38a5852a77e94cb86f19e3a094ae774 100644 (file)
@@ -1599,7 +1599,8 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb,
        flits = skb_transport_offset(skb) / 8;
        sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl;
        sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb),
-                            skb->tail - skb->transport_header,
+                            skb_tail_pointer(skb) -
+                            skb_transport_header(skb),
                             adap->pdev);
        if (need_skb_unmap()) {
                setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits);
index 5f5896e522d2c6068d7b6bc9c685cbc279be48d8..a7a941b1a655a3bbf82baa949c1fe81d17458701 100644 (file)
@@ -158,18 +158,6 @@ static inline board_info_t *to_dm9000_board(struct net_device *dev)
 
 /* DM9000 network board routine ---------------------------- */
 
-static void
-dm9000_reset(board_info_t * db)
-{
-       dev_dbg(db->dev, "resetting device\n");
-
-       /* RESET device */
-       writeb(DM9000_NCR, db->io_addr);
-       udelay(200);
-       writeb(NCR_RST, db->io_data);
-       udelay(200);
-}
-
 /*
  *   Read a byte from I/O port
  */
@@ -191,6 +179,27 @@ iow(board_info_t * db, int reg, int value)
        writeb(value, db->io_data);
 }
 
+static void
+dm9000_reset(board_info_t *db)
+{
+       dev_dbg(db->dev, "resetting device\n");
+
+       /* Reset DM9000, see DM9000 Application Notes V1.22 Jun 11, 2004 page 29
+        * The essential point is that we have to do a double reset, and the
+        * instruction is to set LBK into MAC internal loopback mode.
+        */
+       iow(db, DM9000_NCR, 0x03);
+       udelay(100); /* Application note says at least 20 us */
+       if (ior(db, DM9000_NCR) & 1)
+               dev_err(db->dev, "dm9000 did not respond to first reset\n");
+
+       iow(db, DM9000_NCR, 0);
+       iow(db, DM9000_NCR, 0x03);
+       udelay(100);
+       if (ior(db, DM9000_NCR) & 1)
+               dev_err(db->dev, "dm9000 did not respond to second reset\n");
+}
+
 /* routines for sending block to chip */
 
 static void dm9000_outblk_8bit(void __iomem *reg, void *data, int count)
@@ -744,15 +753,20 @@ static const struct ethtool_ops dm9000_ethtool_ops = {
 static void dm9000_show_carrier(board_info_t *db,
                                unsigned carrier, unsigned nsr)
 {
+       int lpa;
        struct net_device *ndev = db->ndev;
+       struct mii_if_info *mii = &db->mii;
        unsigned ncr = dm9000_read_locked(db, DM9000_NCR);
 
-       if (carrier)
-               dev_info(db->dev, "%s: link up, %dMbps, %s-duplex, no LPA\n",
+       if (carrier) {
+               lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
+               dev_info(db->dev,
+                        "%s: link up, %dMbps, %s-duplex, lpa 0x%04X\n",
                         ndev->name, (nsr & NSR_SPEED) ? 10 : 100,
-                        (ncr & NCR_FDX) ? "full" : "half");
-       else
+                        (ncr & NCR_FDX) ? "full" : "half", lpa);
+       } else {
                dev_info(db->dev, "%s: link down\n", ndev->name);
+       }
 }
 
 static void
@@ -890,9 +904,15 @@ dm9000_init_dm9000(struct net_device *dev)
                        (dev->features & NETIF_F_RXCSUM) ? RCSR_CSUM : 0);
 
        iow(db, DM9000_GPCR, GPCR_GEP_CNTL);    /* Let GPIO0 output */
+       iow(db, DM9000_GPR, 0);
 
-       dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
-       dm9000_phy_write(dev, 0, MII_DM_DSPCR, DSPCR_INIT_PARAM); /* Init */
+       /* If we are dealing with DM9000B, some extra steps are required: a
+        * manual phy reset, and setting init params.
+        */
+       if (db->type == TYPE_DM9000B) {
+               dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET);
+               dm9000_phy_write(dev, 0, MII_DM_DSPCR, DSPCR_INIT_PARAM);
+       }
 
        ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0;
 
index db020230bd0bba5ec72186bed9539357ec315abc..c99dac6a9ddf22131ed9f68e1fe22e018a0c8fcc 100644 (file)
@@ -696,6 +696,15 @@ static inline int qnq_async_evt_rcvd(struct be_adapter *adapter)
        return adapter->flags & BE_FLAGS_QNQ_ASYNC_EVT_RCVD;
 }
 
+static inline int fw_major_num(const char *fw_ver)
+{
+       int fw_major = 0;
+
+       sscanf(fw_ver, "%d.", &fw_major);
+
+       return fw_major;
+}
+
 extern void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm,
                u16 num_popped);
 extern void be_link_status_update(struct be_adapter *adapter, u8 link_status);
index bd0e0c0bbcd8e0e21d5295ef92aea6fb769d2c69..c08fd32bb8e50abd5ba37a7029641df6bfcb80d9 100644 (file)
@@ -1198,7 +1198,6 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
 
        if (lancer_chip(adapter)) {
                req->hdr.version = 1;
-               req->if_id = cpu_to_le16(adapter->if_handle);
        } else if (BEx_chip(adapter)) {
                if (adapter->function_caps & BE_FUNCTION_CAPS_SUPER_NIC)
                        req->hdr.version = 2;
@@ -1206,6 +1205,8 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
                req->hdr.version = 2;
        }
 
+       if (req->hdr.version > 0)
+               req->if_id = cpu_to_le16(adapter->if_handle);
        req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
        req->ulp_num = BE_ULP1_NUM;
        req->type = BE_ETH_TX_RING_TYPE_STANDARD;
index 2c38cc402119c763021ea77461455fd0fa8ac035..53ed58b492c8fb5a7372e68e64eeee5eaaee0e09 100644 (file)
@@ -3247,6 +3247,12 @@ static int be_setup(struct be_adapter *adapter)
 
        be_cmd_get_fw_ver(adapter, adapter->fw_ver, adapter->fw_on_flash);
 
+       if (BE2_chip(adapter) && fw_major_num(adapter->fw_ver) < 4) {
+               dev_err(dev, "Firmware on card is old(%s), IRQs may not work.",
+                       adapter->fw_ver);
+               dev_err(dev, "Please upgrade firmware to version >= 4.0\n");
+       }
+
        if (adapter->vlans_added)
                be_vid_config(adapter);
 
index c4eaadeb572fa3dcd97396afffd961f936d0189d..9fbe4dda7a0e4d68d6bee463fbc6e0a18fa8c061 100644 (file)
@@ -88,6 +88,7 @@
 
 #include <asm/io.h>
 #include <asm/reg.h>
+#include <asm/mpc85xx.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 #include <linux/module.h>
@@ -939,9 +940,8 @@ static void gfar_init_filer_table(struct gfar_private *priv)
        }
 }
 
-static void gfar_detect_errata(struct gfar_private *priv)
+static void __gfar_detect_errata_83xx(struct gfar_private *priv)
 {
-       struct device *dev = &priv->ofdev->dev;
        unsigned int pvr = mfspr(SPRN_PVR);
        unsigned int svr = mfspr(SPRN_SVR);
        unsigned int mod = (svr >> 16) & 0xfff6; /* w/o E suffix */
@@ -957,15 +957,33 @@ static void gfar_detect_errata(struct gfar_private *priv)
            (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
                priv->errata |= GFAR_ERRATA_76;
 
-       /* MPC8313 and MPC837x all rev */
-       if ((pvr == 0x80850010 && mod == 0x80b0) ||
-           (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
-               priv->errata |= GFAR_ERRATA_A002;
+       /* MPC8313 Rev < 2.0 */
+       if (pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020)
+               priv->errata |= GFAR_ERRATA_12;
+}
 
-       /* MPC8313 Rev < 2.0, MPC8548 rev 2.0 */
-       if ((pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020) ||
-           (pvr == 0x80210020 && mod == 0x8030 && rev == 0x0020))
+static void __gfar_detect_errata_85xx(struct gfar_private *priv)
+{
+       unsigned int svr = mfspr(SPRN_SVR);
+
+       if ((SVR_SOC_VER(svr) == SVR_8548) && (SVR_REV(svr) == 0x20))
                priv->errata |= GFAR_ERRATA_12;
+       if (((SVR_SOC_VER(svr) == SVR_P2020) && (SVR_REV(svr) < 0x20)) ||
+           ((SVR_SOC_VER(svr) == SVR_P2010) && (SVR_REV(svr) < 0x20)))
+               priv->errata |= GFAR_ERRATA_76; /* aka eTSEC 20 */
+}
+
+static void gfar_detect_errata(struct gfar_private *priv)
+{
+       struct device *dev = &priv->ofdev->dev;
+
+       /* no plans to fix */
+       priv->errata |= GFAR_ERRATA_A002;
+
+       if (pvr_version_is(PVR_VER_E500V1) || pvr_version_is(PVR_VER_E500V2))
+               __gfar_detect_errata_85xx(priv);
+       else /* non-mpc85xx parts, i.e. e300 core based */
+               __gfar_detect_errata_83xx(priv);
 
        if (priv->errata)
                dev_info(dev, "enabled errata workarounds, flags: 0x%x\n",
@@ -1599,7 +1617,7 @@ static int __gfar_is_rx_idle(struct gfar_private *priv)
        /* Normaly TSEC should not hang on GRS commands, so we should
         * actually wait for IEVENT_GRSC flag.
         */
-       if (likely(!gfar_has_errata(priv, GFAR_ERRATA_A002)))
+       if (!gfar_has_errata(priv, GFAR_ERRATA_A002))
                return 0;
 
        /* Read the eTSEC register at offset 0xD1C. If bits 7-14 are
index dac564c25440cbb5c222a02a8488f8ad6c676e16..e7847510eda26368d5b4f290d83bb8aa7baf179b 100644 (file)
@@ -263,7 +263,9 @@ static inline void mal_schedule_poll(struct mal_instance *mal)
 {
        if (likely(napi_schedule_prep(&mal->napi))) {
                MAL_DBG2(mal, "schedule_poll" NL);
+               spin_lock(&mal->lock);
                mal_disable_eob_irq(mal);
+               spin_unlock(&mal->lock);
                __napi_schedule(&mal->napi);
        } else
                MAL_DBG2(mal, "already in poll" NL);
@@ -442,15 +444,13 @@ static int mal_poll(struct napi_struct *napi, int budget)
                if (unlikely(mc->ops->peek_rx(mc->dev) ||
                             test_bit(MAL_COMMAC_RX_STOPPED, &mc->flags))) {
                        MAL_DBG2(mal, "rotting packet" NL);
-                       if (napi_reschedule(napi))
-                               mal_disable_eob_irq(mal);
-                       else
-                               MAL_DBG2(mal, "already in poll list" NL);
-
-                       if (budget > 0)
-                               goto again;
-                       else
+                       if (!napi_reschedule(napi))
                                goto more_work;
+
+                       spin_lock_irqsave(&mal->lock, flags);
+                       mal_disable_eob_irq(mal);
+                       spin_unlock_irqrestore(&mal->lock, flags);
+                       goto again;
                }
                mc->ops->poll_tx(mc->dev);
        }
index 86d51429a189295d9c7418a1273b625a933d9a9b..151e00cad113f5d7c42249e091a844d56955ba49 100644 (file)
@@ -2655,6 +2655,8 @@ static int igb_set_eee(struct net_device *netdev,
            (hw->phy.media_type != e1000_media_type_copper))
                return -EOPNOTSUPP;
 
+       memset(&eee_curr, 0, sizeof(struct ethtool_eee));
+
        ret_val = igb_get_eee(netdev, &eee_curr);
        if (ret_val)
                return ret_val;
index 7fb5677451f9fb7aee3ebccd31a82b476cfac4df..2c210ec35d59da72e0a55257665538d184bc04ef 100644 (file)
@@ -1131,15 +1131,13 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
        p->rx_discard += rdlp(mp, RX_DISCARD_FRAME_CNT);
        p->rx_overrun += rdlp(mp, RX_OVERRUN_FRAME_CNT);
        spin_unlock_bh(&mp->mib_counters_lock);
-
-       mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ);
 }
 
 static void mib_counters_timer_wrapper(unsigned long _mp)
 {
        struct mv643xx_eth_private *mp = (void *)_mp;
-
        mib_counters_update(mp);
+       mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ);
 }
 
 
@@ -2237,6 +2235,7 @@ static int mv643xx_eth_open(struct net_device *dev)
                mp->int_mask |= INT_TX_END_0 << i;
        }
 
+       add_timer(&mp->mib_counters_timer);
        port_start(mp);
 
        wrlp(mp, INT_MASK_EXT, INT_EXT_LINK_PHY | INT_EXT_TX);
@@ -2534,6 +2533,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
        if (!ppdev)
                return -ENOMEM;
        ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+       ppdev->dev.of_node = pnp;
 
        ret = platform_device_add_resources(ppdev, &res, 1);
        if (ret)
@@ -2916,7 +2916,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        mp->mib_counters_timer.data = (unsigned long)mp;
        mp->mib_counters_timer.function = mib_counters_timer_wrapper;
        mp->mib_counters_timer.expires = jiffies + 30 * HZ;
-       add_timer(&mp->mib_counters_timer);
 
        spin_lock_init(&mp->mib_counters_lock);
 
index ea20182c6969245e53a3a9da563c76461555f7f5..bb11624a1f3948bb81c38238b89422389b43c61b 100644 (file)
@@ -1691,7 +1691,7 @@ static void mlx4_master_deactivate_admin_state(struct mlx4_priv *priv, int slave
                        vp_oper->vlan_idx = NO_INDX;
                }
                if (NO_INDX != vp_oper->mac_idx) {
-                       __mlx4_unregister_mac(&priv->dev, port, vp_oper->mac_idx);
+                       __mlx4_unregister_mac(&priv->dev, port, vp_oper->state.mac);
                        vp_oper->mac_idx = NO_INDX;
                }
        }
index dec455c8f6274a9827f93bf038d8e12c58242765..afe2efa69c8683647766a8d5dc7d561f3f76c2df 100644 (file)
@@ -70,14 +70,15 @@ static int mlx4_alloc_pages(struct mlx4_en_priv *priv,
                put_page(page);
                return -ENOMEM;
        }
-       page_alloc->size = PAGE_SIZE << order;
+       page_alloc->page_size = PAGE_SIZE << order;
        page_alloc->page = page;
        page_alloc->dma = dma;
-       page_alloc->offset = frag_info->frag_align;
+       page_alloc->page_offset = frag_info->frag_align;
        /* Not doing get_page() for each frag is a big win
         * on asymetric workloads.
         */
-       atomic_set(&page->_count, page_alloc->size / frag_info->frag_stride);
+       atomic_set(&page->_count,
+                  page_alloc->page_size / frag_info->frag_stride);
        return 0;
 }
 
@@ -96,16 +97,19 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv,
        for (i = 0; i < priv->num_frags; i++) {
                frag_info = &priv->frag_info[i];
                page_alloc[i] = ring_alloc[i];
-               page_alloc[i].offset += frag_info->frag_stride;
-               if (page_alloc[i].offset + frag_info->frag_stride <= ring_alloc[i].size)
+               page_alloc[i].page_offset += frag_info->frag_stride;
+
+               if (page_alloc[i].page_offset + frag_info->frag_stride <=
+                   ring_alloc[i].page_size)
                        continue;
+
                if (mlx4_alloc_pages(priv, &page_alloc[i], frag_info, gfp))
                        goto out;
        }
 
        for (i = 0; i < priv->num_frags; i++) {
                frags[i] = ring_alloc[i];
-               dma = ring_alloc[i].dma + ring_alloc[i].offset;
+               dma = ring_alloc[i].dma + ring_alloc[i].page_offset;
                ring_alloc[i] = page_alloc[i];
                rx_desc->data[i].addr = cpu_to_be64(dma);
        }
@@ -117,7 +121,7 @@ out:
                frag_info = &priv->frag_info[i];
                if (page_alloc[i].page != ring_alloc[i].page) {
                        dma_unmap_page(priv->ddev, page_alloc[i].dma,
-                               page_alloc[i].size, PCI_DMA_FROMDEVICE);
+                               page_alloc[i].page_size, PCI_DMA_FROMDEVICE);
                        page = page_alloc[i].page;
                        atomic_set(&page->_count, 1);
                        put_page(page);
@@ -131,10 +135,12 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv,
                              int i)
 {
        const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
+       u32 next_frag_end = frags[i].page_offset + 2 * frag_info->frag_stride;
+
 
-       if (frags[i].offset + frag_info->frag_stride > frags[i].size)
-               dma_unmap_page(priv->ddev, frags[i].dma, frags[i].size,
-                                        PCI_DMA_FROMDEVICE);
+       if (next_frag_end > frags[i].page_size)
+               dma_unmap_page(priv->ddev, frags[i].dma, frags[i].page_size,
+                              PCI_DMA_FROMDEVICE);
 
        if (frags[i].page)
                put_page(frags[i].page);
@@ -161,7 +167,7 @@ out:
 
                page_alloc = &ring->page_alloc[i];
                dma_unmap_page(priv->ddev, page_alloc->dma,
-                              page_alloc->size, PCI_DMA_FROMDEVICE);
+                              page_alloc->page_size, PCI_DMA_FROMDEVICE);
                page = page_alloc->page;
                atomic_set(&page->_count, 1);
                put_page(page);
@@ -184,10 +190,11 @@ static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv,
                       i, page_count(page_alloc->page));
 
                dma_unmap_page(priv->ddev, page_alloc->dma,
-                               page_alloc->size, PCI_DMA_FROMDEVICE);
-               while (page_alloc->offset + frag_info->frag_stride < page_alloc->size) {
+                               page_alloc->page_size, PCI_DMA_FROMDEVICE);
+               while (page_alloc->page_offset + frag_info->frag_stride <
+                      page_alloc->page_size) {
                        put_page(page_alloc->page);
-                       page_alloc->offset += frag_info->frag_stride;
+                       page_alloc->page_offset += frag_info->frag_stride;
                }
                page_alloc->page = NULL;
        }
@@ -478,7 +485,7 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
                /* Save page reference in skb */
                __skb_frag_set_page(&skb_frags_rx[nr], frags[nr].page);
                skb_frag_size_set(&skb_frags_rx[nr], frag_info->frag_size);
-               skb_frags_rx[nr].page_offset = frags[nr].offset;
+               skb_frags_rx[nr].page_offset = frags[nr].page_offset;
                skb->truesize += frag_info->frag_stride;
                frags[nr].page = NULL;
        }
@@ -517,7 +524,7 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv,
 
        /* Get pointer to first fragment so we could copy the headers into the
         * (linear part of the) skb */
-       va = page_address(frags[0].page) + frags[0].offset;
+       va = page_address(frags[0].page) + frags[0].page_offset;
 
        if (length <= SMALL_PACKET_SIZE) {
                /* We are copying all relevant data to the skb - temporarily
@@ -645,7 +652,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                        dma_sync_single_for_cpu(priv->ddev, dma, sizeof(*ethh),
                                                DMA_FROM_DEVICE);
                        ethh = (struct ethhdr *)(page_address(frags[0].page) +
-                                                frags[0].offset);
+                                                frags[0].page_offset);
 
                        if (is_multicast_ether_addr(ethh->h_dest)) {
                                struct mlx4_mac_entry *entry;
index 5e0aa569306aab2f6c8185cd1574cdd4abeb4aab..bf06e3610d27787ced2f43b9f9aa9fef105aa3df 100644 (file)
@@ -237,8 +237,8 @@ struct mlx4_en_tx_desc {
 struct mlx4_en_rx_alloc {
        struct page     *page;
        dma_addr_t      dma;
-       u32             offset;
-       u32             size;
+       u32             page_offset;
+       u32             page_size;
 };
 
 struct mlx4_en_tx_ring {
index bd1a2d2bc2aebbad9612d167915fd743cf973c94..ea54d95e5b9f9caa250974b050b5de82a2231e31 100644 (file)
@@ -448,7 +448,8 @@ static int moxart_mac_probe(struct platform_device *pdev)
        irq = irq_of_parse_and_map(node, 0);
        if (irq <= 0) {
                netdev_err(ndev, "irq_of_parse_and_map failed\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto irq_map_fail;
        }
 
        priv = netdev_priv(ndev);
@@ -472,24 +473,32 @@ static int moxart_mac_probe(struct platform_device *pdev)
        priv->tx_desc_base = dma_alloc_coherent(NULL, TX_REG_DESC_SIZE *
                                                TX_DESC_NUM, &priv->tx_base,
                                                GFP_DMA | GFP_KERNEL);
-       if (priv->tx_desc_base == NULL)
+       if (priv->tx_desc_base == NULL) {
+               ret = -ENOMEM;
                goto init_fail;
+       }
 
        priv->rx_desc_base = dma_alloc_coherent(NULL, RX_REG_DESC_SIZE *
                                                RX_DESC_NUM, &priv->rx_base,
                                                GFP_DMA | GFP_KERNEL);
-       if (priv->rx_desc_base == NULL)
+       if (priv->rx_desc_base == NULL) {
+               ret = -ENOMEM;
                goto init_fail;
+       }
 
        priv->tx_buf_base = kmalloc(priv->tx_buf_size * TX_DESC_NUM,
                                    GFP_ATOMIC);
-       if (!priv->tx_buf_base)
+       if (!priv->tx_buf_base) {
+               ret = -ENOMEM;
                goto init_fail;
+       }
 
        priv->rx_buf_base = kmalloc(priv->rx_buf_size * RX_DESC_NUM,
                                    GFP_ATOMIC);
-       if (!priv->rx_buf_base)
+       if (!priv->rx_buf_base) {
+               ret = -ENOMEM;
                goto init_fail;
+       }
 
        platform_set_drvdata(pdev, ndev);
 
@@ -522,7 +531,8 @@ static int moxart_mac_probe(struct platform_device *pdev)
 init_fail:
        netdev_err(ndev, "init failed\n");
        moxart_mac_free_memory(ndev);
-
+irq_map_fail:
+       free_netdev(ndev);
        return ret;
 }
 
index 3ca00e05f23d2ade287d6c1d9047410eeac43114..ace217c447ddd182d0143bb2977b2e62b23ba883 100644 (file)
@@ -2276,9 +2276,9 @@ int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter,
                temp = (cmd.rsp.arg[8] & 0x7FFE0000) >> 17;
                npar_info->max_linkspeed_reg_offset = temp;
        }
-       if (npar_info->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS)
-               memcpy(ahw->extra_capability, &cmd.rsp.arg[16],
-                      sizeof(ahw->extra_capability));
+
+       memcpy(ahw->extra_capability, &cmd.rsp.arg[16],
+              sizeof(ahw->extra_capability));
 
 out:
        qlcnic_free_mbx_args(&cmd);
index ebe4c86e5230223f0c5465485a9812dc1a0aa3b0..ff83a9fcd4c5a844955d221c9daef96772402b7a 100644 (file)
@@ -665,7 +665,7 @@ static int qlcnic_set_channels(struct net_device *dev,
                        return err;
        }
 
-       if (channel->tx_count) {
+       if (qlcnic_82xx_check(adapter) && channel->tx_count) {
                err = qlcnic_validate_max_tx_rings(adapter, channel->tx_count);
                if (err)
                        return err;
index f8adc7b01f1f5ef9e62899a9c68c5eea9d2e2cba..b64e2bef94287cea63a5f452a7e97add7ee73986 100644 (file)
@@ -785,8 +785,6 @@ void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter)
 
 #define QLCNIC_ENABLE_IPV4_LRO         1
 #define QLCNIC_ENABLE_IPV6_LRO         2
-#define QLCNIC_NO_DEST_IPV4_CHECK      (1 << 8)
-#define QLCNIC_NO_DEST_IPV6_CHECK      (2 << 8)
 
 int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
 {
@@ -806,11 +804,10 @@ int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
 
        word = 0;
        if (enable) {
-               word = QLCNIC_ENABLE_IPV4_LRO | QLCNIC_NO_DEST_IPV4_CHECK;
+               word = QLCNIC_ENABLE_IPV4_LRO;
                if (adapter->ahw->extra_capability[0] &
                    QLCNIC_FW_CAP2_HW_LRO_IPV6)
-                       word |= QLCNIC_ENABLE_IPV6_LRO |
-                               QLCNIC_NO_DEST_IPV6_CHECK;
+                       word |= QLCNIC_ENABLE_IPV6_LRO;
        }
 
        req.words[0] = cpu_to_le64(word);
index 21d00a0449a10f394fe6b02d5689c7b0ab3b95cf..d8f4897e9e820ebef4469c2c744e75910f6ebf2c 100644 (file)
@@ -1131,7 +1131,10 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter)
                if (err == -EIO)
                        return err;
                adapter->ahw->extra_capability[0] = temp;
+       } else {
+               adapter->ahw->extra_capability[0] = 0;
        }
+
        adapter->ahw->max_mac_filters = nic_info.max_mac_filters;
        adapter->ahw->max_mtu = nic_info.max_mtu;
 
@@ -2159,8 +2162,7 @@ void qlcnic_set_drv_version(struct qlcnic_adapter *adapter)
        else if (qlcnic_83xx_check(adapter))
                fw_cmd = QLCNIC_CMD_83XX_SET_DRV_VER;
 
-       if ((ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) &&
-           (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_SET_DRV_VER))
+       if (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_SET_DRV_VER)
                qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd);
 }
 
@@ -2257,7 +2259,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        err = qlcnic_alloc_adapter_resources(adapter);
        if (err)
-               goto err_out_free_netdev;
+               goto err_out_free_wq;
 
        adapter->dev_rst_time = jiffies;
        adapter->ahw->revision_id = pdev->revision;
@@ -2396,6 +2398,9 @@ err_out_disable_msi:
 err_out_free_hw:
        qlcnic_free_adapter_resources(adapter);
 
+err_out_free_wq:
+       destroy_workqueue(adapter->qlcnic_wq);
+
 err_out_free_netdev:
        free_netdev(netdev);
 
@@ -3648,11 +3653,6 @@ int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *adapter, u32 txq)
        u8 max_hw = QLCNIC_MAX_TX_RINGS;
        u32 max_allowed;
 
-       if (!qlcnic_82xx_check(adapter)) {
-               netdev_err(netdev, "No Multi TX-Q support\n");
-               return -EINVAL;
-       }
-
        if (!qlcnic_use_msi_x && !qlcnic_use_msi) {
                netdev_err(netdev, "No Multi TX-Q support in INT-x mode\n");
                return -EINVAL;
@@ -3692,8 +3692,7 @@ int qlcnic_validate_max_rss(struct qlcnic_adapter *adapter,
        u8 max_hw = adapter->ahw->max_rx_ques;
        u32 max_allowed;
 
-       if (qlcnic_82xx_check(adapter) && !qlcnic_use_msi_x &&
-           !qlcnic_use_msi) {
+       if (!qlcnic_use_msi_x && !qlcnic_use_msi) {
                netdev_err(netdev, "No RSS support in INT-x mode\n");
                return -EINVAL;
        }
index 5cd831ebfa83b0a95c472926a0105c521f7c352a..b57c278d3b46b2eeb1d9b56b3cc9ca312ba67f61 100644 (file)
@@ -688,12 +688,16 @@ static struct sh_eth_cpu_data r8a7740_data = {
        .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
+       .fdr_value      = 0x0000070f,
+       .rmcr_value     = 0x00000001,
 
        .apr            = 1,
        .mpr            = 1,
        .tpauser        = 1,
        .bculr          = 1,
        .hw_swap        = 1,
+       .rpadir         = 1,
+       .rpadir_value   = 2 << 16,
        .no_trimd       = 1,
        .no_ade         = 1,
        .tsu            = 1,
index 9f18ae984f9ed38386b16e5284d4d10864687c4b..21f9ad6392e9e2d547817c91e5260558eefcbcbb 100644 (file)
@@ -444,6 +444,18 @@ static const struct efx_hw_stat_desc efx_ef10_stat_desc[EF10_STAT_COUNT] = {
        EF10_DMA_STAT(rx_align_error, RX_ALIGN_ERROR_PKTS),
        EF10_DMA_STAT(rx_length_error, RX_LENGTH_ERROR_PKTS),
        EF10_DMA_STAT(rx_nodesc_drops, RX_NODESC_DROPS),
+       EF10_DMA_STAT(rx_pm_trunc_bb_overflow, PM_TRUNC_BB_OVERFLOW),
+       EF10_DMA_STAT(rx_pm_discard_bb_overflow, PM_DISCARD_BB_OVERFLOW),
+       EF10_DMA_STAT(rx_pm_trunc_vfifo_full, PM_TRUNC_VFIFO_FULL),
+       EF10_DMA_STAT(rx_pm_discard_vfifo_full, PM_DISCARD_VFIFO_FULL),
+       EF10_DMA_STAT(rx_pm_trunc_qbb, PM_TRUNC_QBB),
+       EF10_DMA_STAT(rx_pm_discard_qbb, PM_DISCARD_QBB),
+       EF10_DMA_STAT(rx_pm_discard_mapping, PM_DISCARD_MAPPING),
+       EF10_DMA_STAT(rx_dp_q_disabled_packets, RXDP_Q_DISABLED_PKTS),
+       EF10_DMA_STAT(rx_dp_di_dropped_packets, RXDP_DI_DROPPED_PKTS),
+       EF10_DMA_STAT(rx_dp_streaming_packets, RXDP_STREAMING_PKTS),
+       EF10_DMA_STAT(rx_dp_emerg_fetch, RXDP_EMERGENCY_FETCH_CONDITIONS),
+       EF10_DMA_STAT(rx_dp_emerg_wait, RXDP_EMERGENCY_WAIT_CONDITIONS),
 };
 
 #define HUNT_COMMON_STAT_MASK ((1ULL << EF10_STAT_tx_bytes) |          \
@@ -498,44 +510,72 @@ static const struct efx_hw_stat_desc efx_ef10_stat_desc[EF10_STAT_COUNT] = {
 #define HUNT_40G_EXTRA_STAT_MASK ((1ULL << EF10_STAT_rx_align_error) | \
                                  (1ULL << EF10_STAT_rx_length_error))
 
-#if BITS_PER_LONG == 64
-#define STAT_MASK_BITMAP(bits) (bits)
-#else
-#define STAT_MASK_BITMAP(bits) (bits) & 0xffffffff, (bits) >> 32
-#endif
-
-static const unsigned long *efx_ef10_stat_mask(struct efx_nic *efx)
-{
-       static const unsigned long hunt_40g_stat_mask[] = {
-               STAT_MASK_BITMAP(HUNT_COMMON_STAT_MASK |
-                                HUNT_40G_EXTRA_STAT_MASK)
-       };
-       static const unsigned long hunt_10g_only_stat_mask[] = {
-               STAT_MASK_BITMAP(HUNT_COMMON_STAT_MASK |
-                                HUNT_10G_ONLY_STAT_MASK)
-       };
+/* These statistics are only provided if the firmware supports the
+ * capability PM_AND_RXDP_COUNTERS.
+ */
+#define HUNT_PM_AND_RXDP_STAT_MASK (                                   \
+       (1ULL << EF10_STAT_rx_pm_trunc_bb_overflow) |                   \
+       (1ULL << EF10_STAT_rx_pm_discard_bb_overflow) |                 \
+       (1ULL << EF10_STAT_rx_pm_trunc_vfifo_full) |                    \
+       (1ULL << EF10_STAT_rx_pm_discard_vfifo_full) |                  \
+       (1ULL << EF10_STAT_rx_pm_trunc_qbb) |                           \
+       (1ULL << EF10_STAT_rx_pm_discard_qbb) |                         \
+       (1ULL << EF10_STAT_rx_pm_discard_mapping) |                     \
+       (1ULL << EF10_STAT_rx_dp_q_disabled_packets) |                  \
+       (1ULL << EF10_STAT_rx_dp_di_dropped_packets) |                  \
+       (1ULL << EF10_STAT_rx_dp_streaming_packets) |                   \
+       (1ULL << EF10_STAT_rx_dp_emerg_fetch) |                         \
+       (1ULL << EF10_STAT_rx_dp_emerg_wait))
+
+static u64 efx_ef10_raw_stat_mask(struct efx_nic *efx)
+{
+       u64 raw_mask = HUNT_COMMON_STAT_MASK;
        u32 port_caps = efx_mcdi_phy_get_caps(efx);
+       struct efx_ef10_nic_data *nic_data = efx->nic_data;
 
        if (port_caps & (1 << MC_CMD_PHY_CAP_40000FDX_LBN))
-               return hunt_40g_stat_mask;
+               raw_mask |= HUNT_40G_EXTRA_STAT_MASK;
        else
-               return hunt_10g_only_stat_mask;
+               raw_mask |= HUNT_10G_ONLY_STAT_MASK;
+
+       if (nic_data->datapath_caps &
+           (1 << MC_CMD_GET_CAPABILITIES_OUT_PM_AND_RXDP_COUNTERS_LBN))
+               raw_mask |= HUNT_PM_AND_RXDP_STAT_MASK;
+
+       return raw_mask;
+}
+
+static void efx_ef10_get_stat_mask(struct efx_nic *efx, unsigned long *mask)
+{
+       u64 raw_mask = efx_ef10_raw_stat_mask(efx);
+
+#if BITS_PER_LONG == 64
+       mask[0] = raw_mask;
+#else
+       mask[0] = raw_mask & 0xffffffff;
+       mask[1] = raw_mask >> 32;
+#endif
 }
 
 static size_t efx_ef10_describe_stats(struct efx_nic *efx, u8 *names)
 {
+       DECLARE_BITMAP(mask, EF10_STAT_COUNT);
+
+       efx_ef10_get_stat_mask(efx, mask);
        return efx_nic_describe_stats(efx_ef10_stat_desc, EF10_STAT_COUNT,
-                                     efx_ef10_stat_mask(efx), names);
+                                     mask, names);
 }
 
 static int efx_ef10_try_update_nic_stats(struct efx_nic *efx)
 {
        struct efx_ef10_nic_data *nic_data = efx->nic_data;
-       const unsigned long *stats_mask = efx_ef10_stat_mask(efx);
+       DECLARE_BITMAP(mask, EF10_STAT_COUNT);
        __le64 generation_start, generation_end;
        u64 *stats = nic_data->stats;
        __le64 *dma_stats;
 
+       efx_ef10_get_stat_mask(efx, mask);
+
        dma_stats = efx->stats_buffer.addr;
        nic_data = efx->nic_data;
 
@@ -543,8 +583,9 @@ static int efx_ef10_try_update_nic_stats(struct efx_nic *efx)
        if (generation_end == EFX_MC_STATS_GENERATION_INVALID)
                return 0;
        rmb();
-       efx_nic_update_stats(efx_ef10_stat_desc, EF10_STAT_COUNT, stats_mask,
+       efx_nic_update_stats(efx_ef10_stat_desc, EF10_STAT_COUNT, mask,
                             stats, efx->stats_buffer.addr, false);
+       rmb();
        generation_start = dma_stats[MC_CMD_MAC_GENERATION_START];
        if (generation_end != generation_start)
                return -EAGAIN;
@@ -563,12 +604,14 @@ static int efx_ef10_try_update_nic_stats(struct efx_nic *efx)
 static size_t efx_ef10_update_stats(struct efx_nic *efx, u64 *full_stats,
                                    struct rtnl_link_stats64 *core_stats)
 {
-       const unsigned long *mask = efx_ef10_stat_mask(efx);
+       DECLARE_BITMAP(mask, EF10_STAT_COUNT);
        struct efx_ef10_nic_data *nic_data = efx->nic_data;
        u64 *stats = nic_data->stats;
        size_t stats_count = 0, index;
        int retry;
 
+       efx_ef10_get_stat_mask(efx, mask);
+
        /* If we're unlucky enough to read statistics during the DMA, wait
         * up to 10ms for it to finish (typically takes <500us)
         */
index c082562dbf4ee8d96388dc21e0b2da1803fe84f2..366c8e3e37844c8e2d8840a4662467067e937b3b 100644 (file)
@@ -963,7 +963,7 @@ static int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
                               bool *was_attached)
 {
        MCDI_DECLARE_BUF(inbuf, MC_CMD_DRV_ATTACH_IN_LEN);
-       MCDI_DECLARE_BUF(outbuf, MC_CMD_DRV_ATTACH_OUT_LEN);
+       MCDI_DECLARE_BUF(outbuf, MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
        size_t outlen;
        int rc;
 
@@ -981,6 +981,22 @@ static int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
                goto fail;
        }
 
+       /* We currently assume we have control of the external link
+        * and are completely trusted by firmware.  Abort probing
+        * if that's not true for this function.
+        */
+       if (driver_operating &&
+           outlen >= MC_CMD_DRV_ATTACH_EXT_OUT_LEN &&
+           (MCDI_DWORD(outbuf, DRV_ATTACH_EXT_OUT_FUNC_FLAGS) &
+            (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |
+             1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED)) !=
+           (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |
+            1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED)) {
+               netif_err(efx, probe, efx->net_dev,
+                         "This driver version only supports one function per port\n");
+               return -ENODEV;
+       }
+
        if (was_attached != NULL)
                *was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE);
        return 0;
index b5cf62492f8e77ff5bf2a8f0504ae7df4cf8c2ef..e0a63ddb7a6ceb1246dc9a7a7349ce7efffa0d9e 100644 (file)
 #define          MC_CMD_MAC_RX_LANES01_DISP_ERR  0x39 /* enum */
 #define          MC_CMD_MAC_RX_LANES23_DISP_ERR  0x3a /* enum */
 #define          MC_CMD_MAC_RX_MATCH_FAULT  0x3b /* enum */
-#define          MC_CMD_GMAC_DMABUF_START  0x40 /* enum */
-#define          MC_CMD_GMAC_DMABUF_END    0x5f /* enum */
+/* enum: PM trunc_bb_overflow counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_TRUNC_BB_OVERFLOW  0x3c
+/* enum: PM discard_bb_overflow counter. Valid for EF10 with
+ * PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_PM_DISCARD_BB_OVERFLOW  0x3d
+/* enum: PM trunc_vfifo_full counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_TRUNC_VFIFO_FULL  0x3e
+/* enum: PM discard_vfifo_full counter. Valid for EF10 with
+ * PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_PM_DISCARD_VFIFO_FULL  0x3f
+/* enum: PM trunc_qbb counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_TRUNC_QBB  0x40
+/* enum: PM discard_qbb counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_DISCARD_QBB  0x41
+/* enum: PM discard_mapping counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_DISCARD_MAPPING  0x42
+/* enum: RXDP counter: Number of packets dropped due to the queue being
+ * disabled. Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_Q_DISABLED_PKTS  0x43
+/* enum: RXDP counter: Number of packets dropped by the DICPU. Valid for EF10
+ * with PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_DI_DROPPED_PKTS  0x45
+/* enum: RXDP counter: Number of non-host packets. Valid for EF10 with
+ * PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_STREAMING_PKTS  0x46
+/* enum: RXDP counter: Number of times an emergency descriptor fetch was
+ * performed. Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_EMERGENCY_FETCH_CONDITIONS  0x47
+/* enum: RXDP counter: Number of times the DPCPU waited for an existing
+ * descriptor fetch. Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_EMERGENCY_WAIT_CONDITIONS  0x48
+/* enum: Start of GMAC stats buffer space, for Siena only. */
+#define          MC_CMD_GMAC_DMABUF_START  0x40
+/* enum: End of GMAC stats buffer space, for Siena only. */
+#define          MC_CMD_GMAC_DMABUF_END    0x5f
 #define          MC_CMD_MAC_GENERATION_END 0x60 /* enum */
 #define          MC_CMD_MAC_NSTATS  0x61 /* enum */
 
 #define        MC_CMD_GET_CAPABILITIES_OUT_RX_BATCHING_WIDTH 1
 #define        MC_CMD_GET_CAPABILITIES_OUT_MCAST_FILTER_CHAINING_LBN 26
 #define        MC_CMD_GET_CAPABILITIES_OUT_MCAST_FILTER_CHAINING_WIDTH 1
+#define        MC_CMD_GET_CAPABILITIES_OUT_PM_AND_RXDP_COUNTERS_LBN 27
+#define        MC_CMD_GET_CAPABILITIES_OUT_PM_AND_RXDP_COUNTERS_WIDTH 1
 /* RxDPCPU firmware id. */
 #define       MC_CMD_GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID_OFST 4
 #define       MC_CMD_GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID_LEN 2
index e7dbd2dd202e8bb7332b83560f1fa93d56071674..9826594c8a48fa21ca75040fd1513db024092a6e 100644 (file)
@@ -469,8 +469,7 @@ size_t efx_nic_describe_stats(const struct efx_hw_stat_desc *desc, size_t count,
  * @count: Length of the @desc array
  * @mask: Bitmask of which elements of @desc are enabled
  * @stats: Buffer to update with the converted statistics.  The length
- *     of this array must be at least the number of set bits in the
- *     first @count bits of @mask.
+ *     of this array must be at least @count.
  * @dma_buf: DMA buffer containing hardware statistics
  * @accumulate: If set, the converted values will be added rather than
  *     directly stored to the corresponding elements of @stats
@@ -503,11 +502,9 @@ void efx_nic_update_stats(const struct efx_hw_stat_desc *desc, size_t count,
                        }
 
                        if (accumulate)
-                               *stats += val;
+                               stats[index] += val;
                        else
-                               *stats = val;
+                               stats[index] = val;
                }
-
-               ++stats;
        }
 }
index fda29d39032f422d2c395a4fe68a4c5091654006..890bbbe8320ee247ba1ee37965334ed1828d226c 100644 (file)
@@ -386,6 +386,18 @@ enum {
        EF10_STAT_rx_align_error,
        EF10_STAT_rx_length_error,
        EF10_STAT_rx_nodesc_drops,
+       EF10_STAT_rx_pm_trunc_bb_overflow,
+       EF10_STAT_rx_pm_discard_bb_overflow,
+       EF10_STAT_rx_pm_trunc_vfifo_full,
+       EF10_STAT_rx_pm_discard_vfifo_full,
+       EF10_STAT_rx_pm_trunc_qbb,
+       EF10_STAT_rx_pm_discard_qbb,
+       EF10_STAT_rx_pm_discard_mapping,
+       EF10_STAT_rx_dp_q_disabled_packets,
+       EF10_STAT_rx_dp_di_dropped_packets,
+       EF10_STAT_rx_dp_streaming_packets,
+       EF10_STAT_rx_dp_emerg_fetch,
+       EF10_STAT_rx_dp_emerg_wait,
        EF10_STAT_COUNT
 };
 
index 5730fe2445a6c1acf5dfa5cd8767d1b1bb749734..98eedb90cdc3c0220c6f8f4d26d2b7f945cf9624 100644 (file)
@@ -1124,8 +1124,7 @@ static const char * chip_ids[ 16 ] =  {
                        void __iomem *__ioaddr = ioaddr;                \
                        if (__len >= 2 && (unsigned long)__ptr & 2) {   \
                                __len -= 2;                             \
-                               SMC_outw(*(u16 *)__ptr, ioaddr,         \
-                                       DATA_REG(lp));          \
+                               SMC_outsw(ioaddr, DATA_REG(lp), __ptr, 1); \
                                __ptr += 2;                             \
                        }                                               \
                        if (SMC_CAN_USE_DATACS && lp->datacs)           \
@@ -1133,8 +1132,7 @@ static const char * chip_ids[ 16 ] =  {
                        SMC_outsl(__ioaddr, DATA_REG(lp), __ptr, __len>>2); \
                        if (__len & 2) {                                \
                                __ptr += (__len & ~3);                  \
-                               SMC_outw(*((u16 *)__ptr), ioaddr,       \
-                                        DATA_REG(lp));         \
+                               SMC_outsw(ioaddr, DATA_REG(lp), __ptr, 1); \
                        }                                               \
                } else if (SMC_16BIT(lp))                               \
                        SMC_outsw(ioaddr, DATA_REG(lp), p, (l) >> 1);   \
index 79974e31187ac19af63452a3a0c421a5ff2b7cf7..cc3ce557e4aa62074873ee34e348ec24a0719ccf 100644 (file)
@@ -639,13 +639,6 @@ void cpsw_rx_handler(void *token, int len, int status)
 static irqreturn_t cpsw_interrupt(int irq, void *dev_id)
 {
        struct cpsw_priv *priv = dev_id;
-       u32 rx, tx, rx_thresh;
-
-       rx_thresh = __raw_readl(&priv->wr_regs->rx_thresh_stat);
-       rx = __raw_readl(&priv->wr_regs->rx_stat);
-       tx = __raw_readl(&priv->wr_regs->tx_stat);
-       if (!rx_thresh && !rx && !tx)
-               return IRQ_NONE;
 
        cpsw_intr_disable(priv);
        if (priv->irq_enabled == true) {
@@ -1169,9 +1162,9 @@ static int cpsw_ndo_open(struct net_device *ndev)
                }
        }
 
+       napi_enable(&priv->napi);
        cpdma_ctlr_start(priv->dma);
        cpsw_intr_enable(priv);
-       napi_enable(&priv->napi);
        cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX);
        cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX);
 
@@ -1771,8 +1764,8 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
        }
        data->mac_control = prop;
 
-       if (!of_property_read_u32(node, "dual_emac", &prop))
-               data->dual_emac = prop;
+       if (of_property_read_bool(node, "dual_emac"))
+               data->dual_emac = 1;
 
        /*
         * Populate all the child nodes here...
@@ -1782,7 +1775,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
        if (ret)
                pr_warn("Doesn't have any child node\n");
 
-       for_each_node_by_name(slave_node, "slave") {
+       for_each_child_of_node(node, slave_node) {
                struct cpsw_slave_data *slave_data = data->slave_data + i;
                const void *mac_addr = NULL;
                u32 phyid;
@@ -1791,6 +1784,10 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                struct device_node *mdio_node;
                struct platform_device *mdio;
 
+               /* This is no slave child node, continue */
+               if (strcmp(slave_node->name, "slave"))
+                       continue;
+
                parp = of_get_property(slave_node, "phy_id", &lenp);
                if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {
                        pr_err("Missing slave[%d] phy_id property\n", i);
index 67df09ea9d045da26420de1e9da09af58ec0edb8..6a32ef9d63ae2500d5a397a3d6837be525ff7d6f 100644 (file)
@@ -876,8 +876,7 @@ static void emac_dev_mcast_set(struct net_device *ndev)
                    netdev_mc_count(ndev) > EMAC_DEF_MAX_MULTICAST_ADDRESSES) {
                        mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST);
                        emac_add_mcast(priv, EMAC_ALL_MULTI_SET, NULL);
-               }
-               if (!netdev_mc_empty(ndev)) {
+               } else if (!netdev_mc_empty(ndev)) {
                        struct netdev_hw_addr *ha;
 
                        mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST);
index 0721e72f9299250c6c29f0e1d3f7f51affffe7e8..5af1c3e5032addd1441722899d3ab1fb0b010ae9 100644 (file)
@@ -975,7 +975,6 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        return -EINVAL;         /* Cannot change this parameter when up */
                if ((ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_KERNEL)) == NULL)
                        return -ENOBUFS;
-               ym->bitrate = 9600;
                if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs))) {
                        kfree(ym);
                        return -EFAULT;
index 42e6deee6db55ed607170109438960523d6b9b8e..0632d34905c73811456594cf0bf5e27f8710f5c7 100644 (file)
@@ -82,7 +82,6 @@ struct mrf24j40 {
 
        struct mutex buffer_mutex; /* only used to protect buf */
        struct completion tx_complete;
-       struct work_struct irqwork;
        u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */
 };
 
@@ -344,6 +343,8 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (ret)
                goto err;
 
+       INIT_COMPLETION(devrec->tx_complete);
+
        /* Set TXNTRIG bit of TXNCON to send packet */
        ret = read_short_reg(devrec, REG_TXNCON, &val);
        if (ret)
@@ -354,8 +355,6 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
                val |= 0x4;
        write_short_reg(devrec, REG_TXNCON, val);
 
-       INIT_COMPLETION(devrec->tx_complete);
-
        /* Wait for the device to send the TX complete interrupt. */
        ret = wait_for_completion_interruptible_timeout(
                                                &devrec->tx_complete,
@@ -590,17 +589,6 @@ static struct ieee802154_ops mrf24j40_ops = {
 static irqreturn_t mrf24j40_isr(int irq, void *data)
 {
        struct mrf24j40 *devrec = data;
-
-       disable_irq_nosync(irq);
-
-       schedule_work(&devrec->irqwork);
-
-       return IRQ_HANDLED;
-}
-
-static void mrf24j40_isrwork(struct work_struct *work)
-{
-       struct mrf24j40 *devrec = container_of(work, struct mrf24j40, irqwork);
        u8 intstat;
        int ret;
 
@@ -618,7 +606,7 @@ static void mrf24j40_isrwork(struct work_struct *work)
                mrf24j40_handle_rx(devrec);
 
 out:
-       enable_irq(devrec->spi->irq);
+       return IRQ_HANDLED;
 }
 
 static int mrf24j40_probe(struct spi_device *spi)
@@ -642,7 +630,6 @@ static int mrf24j40_probe(struct spi_device *spi)
 
        mutex_init(&devrec->buffer_mutex);
        init_completion(&devrec->tx_complete);
-       INIT_WORK(&devrec->irqwork, mrf24j40_isrwork);
        devrec->spi = spi;
        spi_set_drvdata(spi, devrec);
 
@@ -688,11 +675,12 @@ static int mrf24j40_probe(struct spi_device *spi)
        val &= ~0x3; /* Clear RX mode (normal) */
        write_short_reg(devrec, REG_RXMCR, val);
 
-       ret = request_irq(spi->irq,
-                         mrf24j40_isr,
-                         IRQF_TRIGGER_FALLING,
-                         dev_name(&spi->dev),
-                         devrec);
+       ret = request_threaded_irq(spi->irq,
+                                  NULL,
+                                  mrf24j40_isr,
+                                  IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+                                  dev_name(&spi->dev),
+                                  devrec);
 
        if (ret) {
                dev_err(printdev(devrec), "Unable to get IRQ");
@@ -721,7 +709,6 @@ static int mrf24j40_remove(struct spi_device *spi)
        dev_dbg(printdev(devrec), "remove\n");
 
        free_irq(spi->irq, devrec);
-       flush_work(&devrec->irqwork); /* TODO: Is this the right call? */
        ieee802154_unregister_device(devrec->dev);
        ieee802154_free_device(devrec->dev);
        /* TODO: Will ieee802154_free_device() wait until ->xmit() is
index adeee615dd19f4e0d7c6288466798781a1f8bda7..c9a15925a1f757bf1da6ceef594389df15315f24 100644 (file)
@@ -310,6 +310,7 @@ static ssize_t store_enabled(struct netconsole_target *nt,
                             const char *buf,
                             size_t count)
 {
+       unsigned long flags;
        int enabled;
        int err;
 
@@ -324,9 +325,7 @@ static ssize_t store_enabled(struct netconsole_target *nt,
                return -EINVAL;
        }
 
-       mutex_lock(&nt->mutex);
        if (enabled) {  /* 1 */
-
                /*
                 * Skip netpoll_parse_options() -- all the attributes are
                 * already configured via configfs. Just print them out.
@@ -334,19 +333,22 @@ static ssize_t store_enabled(struct netconsole_target *nt,
                netpoll_print_options(&nt->np);
 
                err = netpoll_setup(&nt->np);
-               if (err) {
-                       mutex_unlock(&nt->mutex);
+               if (err)
                        return err;
-               }
 
                printk(KERN_INFO "netconsole: network logging started\n");
-
        } else {        /* 0 */
+               /* We need to disable the netconsole before cleaning it up
+                * otherwise we might end up in write_msg() with
+                * nt->np.dev == NULL and nt->enabled == 1
+                */
+               spin_lock_irqsave(&target_list_lock, flags);
+               nt->enabled = 0;
+               spin_unlock_irqrestore(&target_list_lock, flags);
                netpoll_cleanup(&nt->np);
        }
 
        nt->enabled = enabled;
-       mutex_unlock(&nt->mutex);
 
        return strnlen(buf, count);
 }
@@ -563,8 +565,10 @@ static ssize_t netconsole_target_attr_store(struct config_item *item,
        struct netconsole_target_attr *na =
                container_of(attr, struct netconsole_target_attr, attr);
 
+       mutex_lock(&nt->mutex);
        if (na->store)
                ret = na->store(nt, buf, count);
+       mutex_unlock(&nt->mutex);
 
        return ret;
 }
index dc920974204e6426610ac5f2fad3a7ba4bd1dd9c..56178761ce930750a0d035a9593dcf0464d10d6d 100644 (file)
@@ -438,17 +438,19 @@ phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
 
        return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
 }
+static DEVICE_ATTR_RO(phy_id);
 
-static struct device_attribute mdio_dev_attrs[] = {
-       __ATTR_RO(phy_id),
-       __ATTR_NULL
+static struct attribute *mdio_dev_attrs[] = {
+       &dev_attr_phy_id.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(mdio_dev);
 
 struct bus_type mdio_bus_type = {
        .name           = "mdio_bus",
        .match          = mdio_bus_match,
        .pm             = MDIO_BUS_PM_OPS,
-       .dev_attrs      = mdio_dev_attrs,
+       .dev_groups     = mdio_dev_groups,
 };
 EXPORT_SYMBOL(mdio_bus_type);
 
index 807815fc996839d14efd18625fb300a2f48d2577..7cb105c103fe9408eb7c02b96dac4f4bfb702456 100644 (file)
@@ -1293,7 +1293,8 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
        if (unlikely(!noblock))
                add_wait_queue(&tfile->wq.wait, &wait);
        while (len) {
-               current->state = TASK_INTERRUPTIBLE;
+               if (unlikely(!noblock))
+                       current->state = TASK_INTERRUPTIBLE;
 
                /* Read frames from the queue */
                if (!(skb = skb_dequeue(&tfile->socket.sk->sk_receive_queue))) {
@@ -1320,9 +1321,10 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
                break;
        }
 
-       current->state = TASK_RUNNING;
-       if (unlikely(!noblock))
+       if (unlikely(!noblock)) {
+               current->state = TASK_RUNNING;
                remove_wait_queue(&tfile->wq.wait, &wait);
+       }
 
        return ret;
 }
index 3569293df8726df8c87786ad83b5be506e6b3285..8e8d0fcd4979f10c71ff1e37140313d255429847 100644 (file)
@@ -36,8 +36,8 @@
 #define AX_RXHDR_L4_TYPE_TCP                   16
 #define AX_RXHDR_L3CSUM_ERR                    2
 #define AX_RXHDR_L4CSUM_ERR                    1
-#define AX_RXHDR_CRC_ERR                       ((u32)BIT(31))
-#define AX_RXHDR_DROP_ERR                      ((u32)BIT(30))
+#define AX_RXHDR_CRC_ERR                       ((u32)BIT(29))
+#define AX_RXHDR_DROP_ERR                      ((u32)BIT(31))
 #define AX_ACCESS_MAC                          0x01
 #define AX_ACCESS_PHY                          0x02
 #define AX_ACCESS_EEPROM                       0x04
@@ -78,7 +78,6 @@
 #define AX_MEDIUM_STATUS_MODE                  0x22
        #define AX_MEDIUM_GIGAMODE      0x01
        #define AX_MEDIUM_FULL_DUPLEX   0x02
-       #define AX_MEDIUM_ALWAYS_ONE    0x04
        #define AX_MEDIUM_EN_125MHZ     0x08
        #define AX_MEDIUM_RXFLOW_CTRLEN 0x10
        #define AX_MEDIUM_TXFLOW_CTRLEN 0x20
@@ -1065,8 +1064,8 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 
        /* Configure default medium type => giga */
        *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
-                AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
-                AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+                AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX |
+                AX_MEDIUM_GIGAMODE;
        ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
                          2, 2, tmp16);
 
@@ -1225,7 +1224,7 @@ static int ax88179_link_reset(struct usbnet *dev)
        }
 
        mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
-              AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE;
+              AX_MEDIUM_RXFLOW_CTRLEN;
 
        ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
                         1, 1, &link_sts);
@@ -1339,8 +1338,8 @@ static int ax88179_reset(struct usbnet *dev)
 
        /* Configure default medium type => giga */
        *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
-                AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
-                AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+                AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX |
+                AX_MEDIUM_GIGAMODE;
        ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
                          2, 2, tmp16);
 
@@ -1406,6 +1405,19 @@ static const struct driver_info sitecom_info = {
        .tx_fixup = ax88179_tx_fixup,
 };
 
+static const struct driver_info samsung_info = {
+       .description = "Samsung USB Ethernet Adapter",
+       .bind = ax88179_bind,
+       .unbind = ax88179_unbind,
+       .status = ax88179_status,
+       .link_reset = ax88179_link_reset,
+       .reset = ax88179_reset,
+       .stop = ax88179_stop,
+       .flags = FLAG_ETHER | FLAG_FRAMING_AX,
+       .rx_fixup = ax88179_rx_fixup,
+       .tx_fixup = ax88179_tx_fixup,
+};
+
 static const struct usb_device_id products[] = {
 {
        /* ASIX AX88179 10/100/1000 */
@@ -1418,7 +1430,11 @@ static const struct usb_device_id products[] = {
 }, {
        /* Sitecom USB 3.0 to Gigabit Adapter */
        USB_DEVICE(0x0df6, 0x0072),
-       .driver_info = (unsigned long) &sitecom_info,
+       .driver_info = (unsigned long)&sitecom_info,
+}, {
+       /* Samsung USB Ethernet Adapter */
+       USB_DEVICE(0x04e8, 0xa100),
+       .driver_info = (unsigned long)&samsung_info,
 },
        { },
 };
index 3d6aaf79d8b2399565b4e9c1a64edd4fe27b90af..818ce90185b5dee9e736923cee09d2b7a0a8d840 100644 (file)
@@ -714,6 +714,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x2357, 0x0201, 4)},    /* TP-LINK HSUPA Modem MA180 */
        {QMI_FIXED_INTF(0x2357, 0x9000, 4)},    /* TP-LINK MA260 */
        {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},    /* Telit LE920 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc005, 6)},    /* Olivetti Olicard 200 */
        {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)},    /* Cinterion PLxx */
 
        /* 4. Gobi 1000 devices */
index bf94e10a37c8e0121d783fc54c1b565b57a7ce21..90a429b7ebad8497d317639389c041d534366255 100644 (file)
@@ -1688,8 +1688,10 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
        if (dev->can_dma_sg && !(info->flags & FLAG_SEND_ZLP) &&
                !(info->flags & FLAG_MULTI_PACKET)) {
                dev->padding_pkt = kzalloc(1, GFP_KERNEL);
-               if (!dev->padding_pkt)
+               if (!dev->padding_pkt) {
+                       status = -ENOMEM;
                        goto out4;
+               }
        }
 
        status = register_netdev (net);
index defec2b3c5a408ff035889d015d717f040e2864c..bbc9cb84ec1fe4f857bc5df63a0ecb3f80659b1b 100644 (file)
@@ -938,7 +938,9 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
                return -EINVAL;
        } else {
                vi->curr_queue_pairs = queue_pairs;
-               schedule_delayed_work(&vi->refill, 0);
+               /* virtnet_open() will refill when device is going to up. */
+               if (dev->flags & IFF_UP)
+                       schedule_delayed_work(&vi->refill, 0);
        }
 
        return 0;
@@ -1128,6 +1130,7 @@ static int virtnet_cpu_callback(struct notifier_block *nfb,
        default:
                break;
        }
+
        return NOTIFY_OK;
 }
 
@@ -1689,6 +1692,8 @@ static int virtnet_freeze(struct virtio_device *vdev)
        struct virtnet_info *vi = vdev->priv;
        int i;
 
+       unregister_hotcpu_notifier(&vi->nb);
+
        /* Prevent config work handler from accessing the device */
        mutex_lock(&vi->config_lock);
        vi->config_enable = false;
@@ -1733,7 +1738,13 @@ static int virtnet_restore(struct virtio_device *vdev)
        vi->config_enable = true;
        mutex_unlock(&vi->config_lock);
 
+       rtnl_lock();
        virtnet_set_queues(vi, vi->curr_queue_pairs);
+       rtnl_unlock();
+
+       err = register_hotcpu_notifier(&vi->nb);
+       if (err)
+               return err;
 
        return 0;
 }
index 3f0c4f268751030318dd920a2a81bf2ddd1d328d..bcfff0d62de4f2070d5ac644a3becfedb74e3462 100644 (file)
@@ -1972,6 +1972,7 @@ fst_get_iface(struct fst_card_info *card, struct fst_port_info *port,
        }
 
        i = port->index;
+       memset(&sync, 0, sizeof(sync));
        sync.clock_rate = FST_RDL(card, portConfig[i].lineSpeed);
        /* Lucky card and linux use same encoding here */
        sync.clock_type = FST_RDB(card, portConfig[i].internalClock) ==
index 5bbcb5e3ee0c0cf5e9fefff42fb6892541e14548..388ddf60a66d492f0df7ea2c6f008b51a7778252 100644 (file)
@@ -148,10 +148,6 @@ static int  enslave( struct net_device *, struct net_device * );
 static int  emancipate( struct net_device * );
 #endif
 
-#ifdef __i386__
-#define ASM_CRC 1
-#endif
-
 static const char  version[] =
        "Granch SBNI12 driver ver 5.0.1  Jun 22 2001  Denis I.Timofeev.\n";
 
@@ -1551,88 +1547,6 @@ __setup( "sbni=", sbni_setup );
 
 /* -------------------------------------------------------------------------- */
 
-#ifdef ASM_CRC
-
-static u32
-calc_crc32( u32  crc,  u8  *p,  u32  len )
-{
-       register u32  _crc;
-       _crc = crc;
-       
-       __asm__ __volatile__ (
-               "xorl   %%ebx, %%ebx\n"
-               "movl   %2, %%esi\n" 
-               "movl   %3, %%ecx\n" 
-               "movl   $crc32tab, %%edi\n"
-               "shrl   $2, %%ecx\n"
-               "jz     1f\n"
-
-               ".align 4\n"
-       "0:\n"
-               "movb   %%al, %%bl\n"
-               "movl   (%%esi), %%edx\n"
-               "shrl   $8, %%eax\n"
-               "xorb   %%dl, %%bl\n"
-               "shrl   $8, %%edx\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   %%dl, %%bl\n"
-               "shrl   $8, %%edx\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   %%dl, %%bl\n"
-               "movb   %%dh, %%dl\n" 
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   %%dl, %%bl\n"
-               "addl   $4, %%esi\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "decl   %%ecx\n"
-               "jnz    0b\n"
-
-       "1:\n"
-               "movl   %3, %%ecx\n"
-               "andl   $3, %%ecx\n"
-               "jz     2f\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   (%%esi), %%bl\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "decl   %%ecx\n"
-               "jz     2f\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   1(%%esi), %%bl\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "decl   %%ecx\n"
-               "jz     2f\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   2(%%esi), %%bl\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-       "2:\n"
-               : "=a" (_crc)
-               : "0" (_crc), "g" (p), "g" (len)
-               : "bx", "cx", "dx", "si", "di"
-       );
-
-       return  _crc;
-}
-
-#else  /* ASM_CRC */
-
 static u32
 calc_crc32( u32  crc,  u8  *p,  u32  len )
 {
@@ -1642,9 +1556,6 @@ calc_crc32( u32  crc,  u8  *p,  u32  len )
        return  crc;
 }
 
-#endif /* ASM_CRC */
-
-
 static u32  crc32tab[] __attribute__ ((aligned(8))) = {
        0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
        0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
index 6a24a5a70cc7d4459e04e0348882f0324dd4fad3..4c0a69779b8980a16ccca5a90acc5ece605cf06a 100644 (file)
@@ -355,6 +355,7 @@ static int wanxl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        ifr->ifr_settings.size = size; /* data size wanted */
                        return -ENOBUFS;
                }
+               memset(&line, 0, sizeof(line));
                line.clock_type = get_status(port)->clocking;
                line.clock_rate = 0;
                line.loopback = 0;
index e4f65900132dedf40a68e2299b2a6177e8dfaf89..709301f88dcd26210a18db4d6eb1f8e230905608 100644 (file)
@@ -208,6 +208,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        unsigned long flags;
+       int i;
 
        if (ath_startrecv(sc) != 0) {
                ath_err(common, "Unable to restart recv logic\n");
@@ -235,6 +236,15 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
                }
        work:
                ath_restart_work(sc);
+
+               for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
+                       if (!ATH_TXQ_SETUP(sc, i))
+                               continue;
+
+                       spin_lock_bh(&sc->tx.txq[i].axq_lock);
+                       ath_txq_schedule(sc, &sc->tx.txq[i]);
+                       spin_unlock_bh(&sc->tx.txq[i].axq_lock);
+               }
        }
 
        ieee80211_wake_queues(sc->hw);
@@ -539,21 +549,10 @@ chip_reset:
 
 static int ath_reset(struct ath_softc *sc)
 {
-       int i, r;
+       int r;
 
        ath9k_ps_wakeup(sc);
-
        r = ath_reset_internal(sc, NULL);
-
-       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
-               if (!ATH_TXQ_SETUP(sc, i))
-                       continue;
-
-               spin_lock_bh(&sc->tx.txq[i].axq_lock);
-               ath_txq_schedule(sc, &sc->tx.txq[i]);
-               spin_unlock_bh(&sc->tx.txq[i].axq_lock);
-       }
-
        ath9k_ps_restore(sc);
 
        return r;
index 5ac713d2ff5d22dc6d976291c6d97098bfbceafd..dd30452df9663574d54024f20eeac031a9910ba3 100644 (file)
@@ -1969,15 +1969,18 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
                               struct ath_atx_tid *tid, struct sk_buff *skb)
 {
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ath_frame_info *fi = get_frame_info(skb);
        struct list_head bf_head;
-       struct ath_buf *bf;
-
-       bf = fi->bf;
+       struct ath_buf *bf = fi->bf;
 
        INIT_LIST_HEAD(&bf_head);
        list_add_tail(&bf->list, &bf_head);
        bf->bf_state.bf_type = 0;
+       if (tid && (tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
+               bf->bf_state.bf_type = BUF_AMPDU;
+               ath_tx_addto_baw(sc, tid, bf);
+       }
 
        bf->bf_next = NULL;
        bf->bf_lastbf = bf;
index 899cad34ccd3aa1649029d90294fe21c30608d14..755a0c8edfe1235e73180b8b45d6143afa582547 100644 (file)
@@ -237,7 +237,9 @@ static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
        struct hwbus_priv *self = dev_id;
 
        if (self->core) {
+               cw1200_spi_lock(self);
                cw1200_irq_handler(self->core);
+               cw1200_spi_unlock(self);
                return IRQ_HANDLED;
        } else {
                return IRQ_NONE;
index 30d45e2fc193a5bc04987acbdef9b0252a2b5c01..8ac305be68f489be533606cb8260dbfd4dc5cfda 100644 (file)
@@ -240,6 +240,12 @@ const struct iwl_cfg iwl6035_2agn_cfg = {
        .ht_params = &iwl6000_ht_params,
 };
 
+const struct iwl_cfg iwl6035_2agn_sff_cfg = {
+       .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN",
+       IWL_DEVICE_6035,
+       .ht_params = &iwl6000_ht_params,
+};
+
 const struct iwl_cfg iwl1030_bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN",
        IWL_DEVICE_6030,
index e4d370bff30679fceacc1bc82907ccb6bc194f22..b03c25e14903d05c150c176d608f4be1f654673e 100644 (file)
@@ -280,6 +280,7 @@ extern const struct iwl_cfg iwl2000_2bgn_cfg;
 extern const struct iwl_cfg iwl2000_2bgn_d_cfg;
 extern const struct iwl_cfg iwl2030_2bgn_cfg;
 extern const struct iwl_cfg iwl6035_2agn_cfg;
+extern const struct iwl_cfg iwl6035_2agn_sff_cfg;
 extern const struct iwl_cfg iwl105_bgn_cfg;
 extern const struct iwl_cfg iwl105_bgn_d_cfg;
 extern const struct iwl_cfg iwl135_bgn_cfg;
index dd57a36ecb1005d7571290a59925fd4d641b61aa..80b47508647ca8d5861f4b4c8dc8e8ec879d34c3 100644 (file)
@@ -601,8 +601,10 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
 {
        int ret;
 
-       WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
-                 "%s bad state = %d", __func__, trans->state);
+       if (trans->state != IWL_TRANS_FW_ALIVE) {
+               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               return -EIO;
+       }
 
        if (!(cmd->flags & CMD_ASYNC))
                lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
index 21407a353a3b0e623c87ae3627957ae45ae3b162..d58e393324ef3ff9591f1cf297cf656c259037ae 100644 (file)
@@ -273,7 +273,10 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                if (!mvmvif->queue_params[ac].uapsd)
                        continue;
 
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+               if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+
                cmd->uapsd_ac_flags |= BIT(ac);
 
                /* QNDP TID - the highest TID with no admission control */
index 9a7ab84953000234b463ac1636cabcf506b95433..621fb71f282a88e9a285bb3d4b2b26a1b2fef275 100644 (file)
@@ -394,6 +394,11 @@ static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
                        return false;
                }
 
+               /*
+                * If scan cannot be aborted, it means that we had a
+                * SCAN_COMPLETE_NOTIFICATION in the pipe and it called
+                * ieee80211_scan_completed already.
+                */
                IWL_DEBUG_SCAN(mvm, "Scan cannot be aborted, exit now: %d\n",
                               *resp);
                return true;
@@ -417,14 +422,19 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
                                               SCAN_COMPLETE_NOTIFICATION };
        int ret;
 
+       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+               return;
+
        iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
                                   scan_abort_notif,
                                   ARRAY_SIZE(scan_abort_notif),
                                   iwl_mvm_scan_abort_notif, NULL);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD,
+                                  CMD_SYNC | CMD_SEND_IN_RFKILL, 0, NULL);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
+               /* mac80211's state will be cleaned in the fw_restart flow */
                goto out_remove_notif;
        }
 
index dc02cb9792afbbb48c23f01d748c262bee1828fd..26108a1a29fa6d6871cea350aae20820c27d4220 100644 (file)
@@ -139,13 +139,16 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 
 /* 6x00 Series */
        {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
+       {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)},
        {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
+       {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
        {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
+       {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)},
        {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
        {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
 
@@ -153,12 +156,16 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)},
+       {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)},
+       {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)},
+       {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)},
+       {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */
        {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */
@@ -240,8 +247,11 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 
 /* 6x35 Series */
        {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)},
+       {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)},
+       {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)},
+       {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)},
        {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)},
 
@@ -260,54 +270,86 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 #if IS_ENABLED(CONFIG_IWLMVM)
 /* 7000 Series */
        {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
        {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
        {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)},
 
 /* 3160 Series */
        {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)},
        {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
        {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)},
 #endif /* CONFIG_IWLMVM */
 
        {0}
index bad95d28d50da52158ff102de3fc9855e46a662b..c3f904d422b08b1074345fcba99a31390f663945 100644 (file)
@@ -1401,6 +1401,10 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        spin_lock_init(&trans_pcie->reg_lock);
        init_waitqueue_head(&trans_pcie->ucode_write_waitq);
 
+       err = pci_enable_device(pdev);
+       if (err)
+               goto out_no_pci;
+
        if (!cfg->base_params->pcie_l1_allowed) {
                /*
                 * W/A - seems to solve weird behavior. We need to remove this
@@ -1412,10 +1416,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
                                       PCIE_LINK_STATE_CLKPM);
        }
 
-       err = pci_enable_device(pdev);
-       if (err)
-               goto out_no_pci;
-
        pci_set_master(pdev);
 
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
index f45eb29c2ede0b62cc1723f82ae7f30cc1f5a8ed..1424335163b97e4d6625a8fca7ec7c19a1d07c9c 100644 (file)
@@ -1102,6 +1102,8 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
                 * non-AGG queue.
                 */
                iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+
+               ssn = trans_pcie->txq[txq_id].q.read_ptr;
        }
 
        /* Place first TFD at index corresponding to start sequence number.
index 9d7c0e6c4fc7419facd68a3c61f251fbedb86143..37f873bb342f5043046b18c3fe3b573ebdedac64 100644 (file)
@@ -1422,13 +1422,19 @@ static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac)
  */
 int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
 {
+       int ret = 0;
+
        if (!priv->media_connected)
                return 0;
 
        switch (priv->bss_mode) {
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
-               return mwifiex_deauthenticate_infra(priv, mac);
+               ret = mwifiex_deauthenticate_infra(priv, mac);
+               if (ret)
+                       cfg80211_disconnected(priv->netdev, 0, NULL, 0,
+                                             GFP_KERNEL);
+               break;
        case NL80211_IFTYPE_ADHOC:
                return mwifiex_send_cmd_sync(priv,
                                             HostCmd_CMD_802_11_AD_HOC_STOP,
@@ -1440,7 +1446,7 @@ int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
                break;
        }
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(mwifiex_deauthenticate);
 
index fd778337deeec4e58b1bddb4d08668aaf51890d9..c2b91f566e05073d83dbbe60e265a78ddaf8fa9e 100644 (file)
@@ -358,10 +358,12 @@ process_start:
                }
        } while (true);
 
-       if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter))
+       spin_lock_irqsave(&adapter->main_proc_lock, flags);
+       if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter)) {
+               spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
                goto process_start;
+       }
 
-       spin_lock_irqsave(&adapter->main_proc_lock, flags);
        adapter->mwifiex_processing = false;
        spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
 
index 8b057524b252e535307644ddaa98e12df75a35cd..8c351f71f72f9f6f6a17650198ba805bf7c1f5cd 100644 (file)
@@ -118,7 +118,8 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
        dev_dbg(adapter->dev,
                "info: successfully disconnected from %pM: reason code %d\n",
                priv->cfg_bssid, reason_code);
-       if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+       if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+           priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
                cfg80211_disconnected(priv->netdev, reason_code, NULL, 0,
                                      GFP_KERNEL);
        }
index 76d95deb274be56feb9803adb5b8d198f0c35bd0..dc49e525ae5e55e9a090308b80122998e08857d8 100644 (file)
@@ -105,13 +105,11 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
                goto exit_release_regions;
        }
 
-       pci_enable_msi(pci_dev);
-
        hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
        if (!hw) {
                rt2x00_probe_err("Failed to allocate hardware\n");
                retval = -ENOMEM;
-               goto exit_disable_msi;
+               goto exit_release_regions;
        }
 
        pci_set_drvdata(pci_dev, hw);
@@ -152,9 +150,6 @@ exit_free_reg:
 exit_free_device:
        ieee80211_free_hw(hw);
 
-exit_disable_msi:
-       pci_disable_msi(pci_dev);
-
 exit_release_regions:
        pci_release_regions(pci_dev);
 
@@ -179,8 +174,6 @@ void rt2x00pci_remove(struct pci_dev *pci_dev)
        rt2x00pci_free_reg(rt2x00dev);
        ieee80211_free_hw(hw);
 
-       pci_disable_msi(pci_dev);
-
        /*
         * Free the PCI device data.
         */
index 763cf1defab5b4027b22604c3771e4c8465a4405..5a060e537fbe99171f234c29a6320f75ad22a1f3 100644 (file)
@@ -343,7 +343,8 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw,
                                        (bool)GET_RX_DESC_PAGGR(pdesc));
        rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
        if (phystatus) {
-               p_drvinfo = (struct rx_fwinfo_92c *)(pdesc + RTL_RX_DESC_SIZE);
+               p_drvinfo = (struct rx_fwinfo_92c *)(skb->data +
+                                                    stats->rx_bufshift);
                rtl92c_translate_rx_signal_stuff(hw, skb, stats, pdesc,
                                                 p_drvinfo);
        }
index 5715318d6bab3b4c7905c7c69ee549e39636c6cc..400fea1de080c163f18fbe8efa3048a47abe6ec1 100644 (file)
@@ -163,6 +163,7 @@ struct xenvif {
        unsigned long   credit_usec;
        unsigned long   remaining_credit;
        struct timer_list credit_timeout;
+       u64 credit_window_start;
 
        /* Statistics */
        unsigned long rx_gso_checksum_fixup;
index 01bb854c7f62bfc281dfdc0081829237f2f843d6..459935a6bfae3ab31c634feff974e64f8f93cf3d 100644 (file)
@@ -312,8 +312,7 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
        vif->credit_bytes = vif->remaining_credit = ~0UL;
        vif->credit_usec  = 0UL;
        init_timer(&vif->credit_timeout);
-       /* Initialize 'expires' now: it's used to track the credit window. */
-       vif->credit_timeout.expires = jiffies;
+       vif->credit_window_start = get_jiffies_64();
 
        dev->netdev_ops = &xenvif_netdev_ops;
        dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
index f3e591c611ded744ec0b6ea2cefe9b7dd3d568c7..900da4b243ad7a0a4eb21ae992d52912650f4c5c 100644 (file)
@@ -1185,9 +1185,8 @@ out:
 
 static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
 {
-       unsigned long now = jiffies;
-       unsigned long next_credit =
-               vif->credit_timeout.expires +
+       u64 now = get_jiffies_64();
+       u64 next_credit = vif->credit_window_start +
                msecs_to_jiffies(vif->credit_usec / 1000);
 
        /* Timer could already be pending in rare cases. */
@@ -1195,8 +1194,8 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
                return true;
 
        /* Passed the point where we can replenish credit? */
-       if (time_after_eq(now, next_credit)) {
-               vif->credit_timeout.expires = now;
+       if (time_after_eq64(now, next_credit)) {
+               vif->credit_window_start = now;
                tx_add_credit(vif);
        }
 
@@ -1208,6 +1207,7 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
                        tx_credit_callback;
                mod_timer(&vif->credit_timeout,
                          next_credit);
+               vif->credit_window_start = next_credit;
 
                return true;
        }
index b45bce20ad7624421b7500cff3ccfc5a49f9204f..1b08d879837231256f4cc954bbe8fbd0c4aa9391 100644 (file)
@@ -39,11 +39,15 @@ static int connect_rings(struct backend_info *);
 static void connect(struct backend_info *);
 static void backend_create_xenvif(struct backend_info *be);
 static void unregister_hotplug_status_watch(struct backend_info *be);
+static void set_backend_state(struct backend_info *be,
+                             enum xenbus_state state);
 
 static int netback_remove(struct xenbus_device *dev)
 {
        struct backend_info *be = dev_get_drvdata(&dev->dev);
 
+       set_backend_state(be, XenbusStateClosed);
+
        unregister_hotplug_status_watch(be);
        if (be->vif) {
                kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
index be12fbfcae1042e90c00d192bffe46c47821b6c4..1ea75236a15fcd800006612e9cac50c0f0e48f5a 100644 (file)
@@ -552,9 +552,8 @@ static void __ref enable_slot(struct acpiphp_slot *slot)
        struct acpiphp_func *func;
        int max, pass;
        LIST_HEAD(add_list);
-       int nr_found;
 
-       nr_found = acpiphp_rescan_slot(slot);
+       acpiphp_rescan_slot(slot);
        max = acpiphp_max_busnr(bus);
        for (pass = 0; pass < 2; pass++) {
                list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -574,9 +573,6 @@ static void __ref enable_slot(struct acpiphp_slot *slot)
                }
        }
        __pci_bus_assign_resources(bus, &add_list, NULL);
-       /* Nothing more to do here if there are no new devices on this bus. */
-       if (!nr_found && (slot->flags & SLOT_ENABLED))
-               return;
 
        acpiphp_sanitize_bus(bus);
        acpiphp_set_hpp_values(bus);
index 98f7b9b89507e05a331cceacc8defad13e7d9534..38f3c0140dfb6f411cdb4f1ba72133bc8303e48e 100644 (file)
@@ -135,6 +135,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
                return retval;
        return count;
 }
+static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
 
 /**
  * store_remove_id - remove a PCI device ID from this driver
@@ -180,12 +181,14 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
                return retval;
        return count;
 }
+static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
 
-static struct driver_attribute pci_drv_attrs[] = {
-       __ATTR(new_id, S_IWUSR, NULL, store_new_id),
-       __ATTR(remove_id, S_IWUSR, NULL, store_remove_id),
-       __ATTR_NULL,
+static struct attribute *pci_drv_attrs[] = {
+       &driver_attr_new_id.attr,
+       &driver_attr_remove_id.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(pci_drv);
 
 /**
  * pci_match_id - See if a pci device matches a given pci_id table
@@ -1317,8 +1320,8 @@ struct bus_type pci_bus_type = {
        .remove         = pci_device_remove,
        .shutdown       = pci_device_shutdown,
        .dev_attrs      = pci_dev_attrs,
-       .bus_attrs      = pci_bus_attrs,
-       .drv_attrs      = pci_drv_attrs,
+       .bus_groups     = pci_bus_groups,
+       .drv_groups     = pci_drv_groups,
        .pm             = PCI_PM_OPS_PTR,
 };
 
index 7128cfdd64aa9d31c8628be30e7c65b33396dbfb..d8eb880bd1fce5971774a03719eb3be14e83f690 100644 (file)
@@ -302,10 +302,20 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
        }
        return count;
 }
+static BUS_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store);
 
-struct bus_attribute pci_bus_attrs[] = {
-       __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store),
-       __ATTR_NULL
+struct attribute *pci_bus_attrs[] = {
+       &bus_attr_rescan.attr,
+       NULL,
+};
+
+static const struct attribute_group pci_bus_group = {
+       .attrs = pci_bus_attrs,
+};
+
+const struct attribute_group *pci_bus_groups[] = {
+       &pci_bus_group,
+       NULL,
 };
 
 static ssize_t
index 8a00c063d7bc67af7a3256fdd4a81c3d4d87f80b..607be58dd72859ab40384c49d0de8a84f49c24a7 100644 (file)
@@ -156,7 +156,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev)
 extern struct device_attribute pci_dev_attrs[];
 extern const struct attribute_group *pcibus_groups[];
 extern struct device_type pci_dev_type;
-extern struct bus_attribute pci_bus_attrs[];
+extern const struct attribute_group *pci_bus_groups[];
 
 
 /**
index b8f5acf02261ca6708b26692d420f9164d2ac046..de24232c5191040fa0aa3607d12a7d3fd2a0d4ae 100644 (file)
@@ -245,7 +245,7 @@ static int at91_cf_dt_init(struct platform_device *pdev)
 }
 #endif
 
-static int __init at91_cf_probe(struct platform_device *pdev)
+static int at91_cf_probe(struct platform_device *pdev)
 {
        struct at91_cf_socket   *cf;
        struct at91_cf_data     *board = pdev->dev.platform_data;
@@ -354,7 +354,7 @@ fail0a:
        return status;
 }
 
-static int __exit at91_cf_remove(struct platform_device *pdev)
+static int at91_cf_remove(struct platform_device *pdev)
 {
        struct at91_cf_socket   *cf = platform_get_drvdata(pdev);
 
@@ -404,14 +404,13 @@ static struct platform_driver at91_cf_driver = {
                .owner          = THIS_MODULE,
                .of_match_table = of_match_ptr(at91_cf_dt_ids),
        },
-       .remove         = __exit_p(at91_cf_remove),
+       .probe          = at91_cf_probe,
+       .remove         = at91_cf_remove,
        .suspend        = at91_cf_suspend,
        .resume         = at91_cf_resume,
 };
 
-/*--------------------------------------------------------------------------*/
-
-module_platform_driver_probe(at91_cf_driver, at91_cf_probe);
+module_platform_driver(at91_cf_driver);
 
 MODULE_DESCRIPTION("AT91 Compact Flash Driver");
 MODULE_AUTHOR("David Brownell");
index 2deacbb2ffdc4d325b9dea851d8d2dc56a44191a..757119b87146cbc5219527c8e7c584795c34174e 100644 (file)
@@ -992,16 +992,17 @@ static ssize_t field##_show (struct device *dev, struct device_attribute *attr,
 {                                                                      \
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);               \
        return p_dev->test ? sprintf(buf, format, p_dev->field) : -ENODEV; \
-}
+}                                                                      \
+static DEVICE_ATTR_RO(field);
 
 #define pcmcia_device_stringattr(name, field)                                  \
 static ssize_t name##_show (struct device *dev, struct device_attribute *attr, char *buf)              \
 {                                                                      \
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);               \
        return p_dev->field ? sprintf(buf, "%s\n", p_dev->field) : -ENODEV; \
-}
+}                                                                      \
+static DEVICE_ATTR_RO(name);
 
-pcmcia_device_attr(func, socket, "0x%02x\n");
 pcmcia_device_attr(func_id, has_func_id, "0x%02x\n");
 pcmcia_device_attr(manf_id, has_manf_id, "0x%04x\n");
 pcmcia_device_attr(card_id, has_card_id, "0x%04x\n");
@@ -1010,8 +1011,16 @@ pcmcia_device_stringattr(prod_id2, prod_id[1]);
 pcmcia_device_stringattr(prod_id3, prod_id[2]);
 pcmcia_device_stringattr(prod_id4, prod_id[3]);
 
-static ssize_t pcmcia_show_resources(struct device *dev,
-                                    struct device_attribute *attr, char *buf)
+static ssize_t function_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+       return p_dev->socket ? sprintf(buf, "0x%02x\n", p_dev->func) : -ENODEV;
+}
+static DEVICE_ATTR_RO(function);
+
+static ssize_t resources_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
        char *str = buf;
@@ -1022,8 +1031,9 @@ static ssize_t pcmcia_show_resources(struct device *dev,
 
        return str - buf;
 }
+static DEVICE_ATTR_RO(resources);
 
-static ssize_t pcmcia_show_pm_state(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t pm_state_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
 
@@ -1033,8 +1043,8 @@ static ssize_t pcmcia_show_pm_state(struct device *dev, struct device_attribute
                return sprintf(buf, "on\n");
 }
 
-static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute *attr,
-                                    const char *buf, size_t count)
+static ssize_t pm_state_store(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t count)
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
        int ret = 0;
@@ -1049,7 +1059,7 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute
 
        return ret ? ret : count;
 }
-
+static DEVICE_ATTR_RW(pm_state);
 
 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
@@ -1072,8 +1082,9 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
                                p_dev->func, p_dev->device_no,
                                hash[0], hash[1], hash[2], hash[3]);
 }
+static DEVICE_ATTR_RO(modalias);
 
-static ssize_t pcmcia_store_allow_func_id_match(struct device *dev,
+static ssize_t allow_func_id_match_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
@@ -1088,22 +1099,24 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev,
 
        return count;
 }
-
-static struct device_attribute pcmcia_dev_attrs[] = {
-       __ATTR(function, 0444, func_show, NULL),
-       __ATTR(pm_state, 0644, pcmcia_show_pm_state, pcmcia_store_pm_state),
-       __ATTR(resources, 0444, pcmcia_show_resources, NULL),
-       __ATTR_RO(func_id),
-       __ATTR_RO(manf_id),
-       __ATTR_RO(card_id),
-       __ATTR_RO(prod_id1),
-       __ATTR_RO(prod_id2),
-       __ATTR_RO(prod_id3),
-       __ATTR_RO(prod_id4),
-       __ATTR_RO(modalias),
-       __ATTR(allow_func_id_match, 0200, NULL, pcmcia_store_allow_func_id_match),
-       __ATTR_NULL,
+static DEVICE_ATTR_WO(allow_func_id_match);
+
+static struct attribute *pcmcia_dev_attrs[] = {
+       &dev_attr_resources.attr,
+       &dev_attr_pm_state.attr,
+       &dev_attr_function.attr,
+       &dev_attr_func_id.attr,
+       &dev_attr_manf_id.attr,
+       &dev_attr_card_id.attr,
+       &dev_attr_prod_id1.attr,
+       &dev_attr_prod_id2.attr,
+       &dev_attr_prod_id3.attr,
+       &dev_attr_prod_id4.attr,
+       &dev_attr_modalias.attr,
+       &dev_attr_allow_func_id_match.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(pcmcia_dev);
 
 /* PM support, also needed for reset */
 
@@ -1389,7 +1402,7 @@ struct bus_type pcmcia_bus_type = {
        .name = "pcmcia",
        .uevent = pcmcia_bus_uevent,
        .match = pcmcia_bus_match,
-       .dev_attrs = pcmcia_dev_attrs,
+       .dev_groups = pcmcia_dev_groups,
        .probe = pcmcia_device_probe,
        .remove = pcmcia_device_remove,
        .suspend = pcmcia_dev_suspend,
index a4c16ee5c7188d24bce41f834851f61f5e56d9ea..622dd6fe7347c7e3aec8fb3d337347ec8bd03537 100644 (file)
@@ -777,15 +777,4 @@ static struct pci_driver pd6729_pci_driver = {
        .remove         = pd6729_pci_remove,
 };
 
-static int pd6729_module_init(void)
-{
-       return pci_register_driver(&pd6729_pci_driver);
-}
-
-static void pd6729_module_exit(void)
-{
-       pci_unregister_driver(&pd6729_pci_driver);
-}
-
-module_init(pd6729_module_init);
-module_exit(pd6729_module_exit);
+module_pci_driver(pd6729_pci_driver);
index 6b4ff099fb13c8de403f20e1920c002787c8a81d..dc18a3a5e010479756545400a3c84f7dc0409685 100644 (file)
@@ -1439,20 +1439,6 @@ static struct pci_driver yenta_cardbus_driver = {
        .driver.pm      = YENTA_PM_OPS,
 };
 
-
-static int __init yenta_socket_init(void)
-{
-       return pci_register_driver(&yenta_cardbus_driver);
-}
-
-
-static void __exit yenta_socket_exit(void)
-{
-       pci_unregister_driver(&yenta_cardbus_driver);
-}
-
-
-module_init(yenta_socket_init);
-module_exit(yenta_socket_exit);
+module_pci_driver(yenta_cardbus_driver);
 
 MODULE_LICENSE("GPL");
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
new file mode 100644 (file)
index 0000000..a344f3d
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# PHY
+#
+
+menu "PHY Subsystem"
+
+config GENERIC_PHY
+       tristate "PHY Core"
+       help
+         Generic PHY support.
+
+         This framework is designed to provide a generic interface for PHY
+         devices present in the kernel. This layer will have the generic
+         API by which phy drivers can create PHY using the phy framework and
+         phy users can obtain reference to the PHY. All the users of this
+         framework should select this config.
+
+config PHY_EXYNOS_MIPI_VIDEO
+       tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
+       help
+         Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
+         and EXYNOS SoCs.
+
+config OMAP_USB2
+       tristate "OMAP USB2 PHY Driver"
+       depends on ARCH_OMAP2PLUS
+       select GENERIC_PHY
+       select USB_PHY
+       select OMAP_CONTROL_USB
+       help
+         Enable this to support the transceiver that is part of SOC. This
+         driver takes care of all the PHY functionality apart from comparator.
+         The USB OTG controller communicates with the comparator using this
+         driver.
+
+config TWL4030_USB
+       tristate "TWL4030 USB Transceiver Driver"
+       depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
+       select GENERIC_PHY
+       select USB_PHY
+       help
+         Enable this to support the USB OTG transceiver on TWL4030
+         family chips (including the TWL5030 and TPS659x0 devices).
+         This transceiver supports high and full speed devices plus,
+         in host mode, low speed.
+
+config PHY_EXYNOS_DP_VIDEO
+       tristate "EXYNOS SoC series Display Port PHY driver"
+       depends on OF
+       select GENERIC_PHY
+       help
+         Support for Display Port PHY found on Samsung EXYNOS SoCs.
+
+endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
new file mode 100644 (file)
index 0000000..d0caae9
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for the phy drivers.
+#
+
+obj-$(CONFIG_GENERIC_PHY)              += phy-core.o
+obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)      += phy-exynos-dp-video.o
+obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)    += phy-exynos-mipi-video.o
+obj-$(CONFIG_OMAP_USB2)                        += phy-omap-usb2.o
+obj-$(CONFIG_TWL4030_USB)              += phy-twl4030-usb.o
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
new file mode 100644 (file)
index 0000000..03cf8fb
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ * phy-core.c  --  Generic Phy framework.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+
+static struct class *phy_class;
+static DEFINE_MUTEX(phy_provider_mutex);
+static LIST_HEAD(phy_provider_list);
+static DEFINE_IDA(phy_ida);
+
+static void devm_phy_release(struct device *dev, void *res)
+{
+       struct phy *phy = *(struct phy **)res;
+
+       phy_put(phy);
+}
+
+static void devm_phy_provider_release(struct device *dev, void *res)
+{
+       struct phy_provider *phy_provider = *(struct phy_provider **)res;
+
+       of_phy_provider_unregister(phy_provider);
+}
+
+static void devm_phy_consume(struct device *dev, void *res)
+{
+       struct phy *phy = *(struct phy **)res;
+
+       phy_destroy(phy);
+}
+
+static int devm_phy_match(struct device *dev, void *res, void *match_data)
+{
+       return res == match_data;
+}
+
+static struct phy *phy_lookup(struct device *device, const char *port)
+{
+       unsigned int count;
+       struct phy *phy;
+       struct device *dev;
+       struct phy_consumer *consumers;
+       struct class_dev_iter iter;
+
+       class_dev_iter_init(&iter, phy_class, NULL, NULL);
+       while ((dev = class_dev_iter_next(&iter))) {
+               phy = to_phy(dev);
+               count = phy->init_data->num_consumers;
+               consumers = phy->init_data->consumers;
+               while (count--) {
+                       if (!strcmp(consumers->dev_name, dev_name(device)) &&
+                                       !strcmp(consumers->port, port)) {
+                               class_dev_iter_exit(&iter);
+                               return phy;
+                       }
+                       consumers++;
+               }
+       }
+
+       class_dev_iter_exit(&iter);
+       return ERR_PTR(-ENODEV);
+}
+
+static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
+{
+       struct phy_provider *phy_provider;
+
+       list_for_each_entry(phy_provider, &phy_provider_list, list) {
+               if (phy_provider->dev->of_node == node)
+                       return phy_provider;
+       }
+
+       return ERR_PTR(-EPROBE_DEFER);
+}
+
+int phy_pm_runtime_get(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_get(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get);
+
+int phy_pm_runtime_get_sync(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_get_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync);
+
+int phy_pm_runtime_put(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_put(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put);
+
+int phy_pm_runtime_put_sync(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_put_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync);
+
+void phy_pm_runtime_allow(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return;
+
+       pm_runtime_allow(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_allow);
+
+void phy_pm_runtime_forbid(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return;
+
+       pm_runtime_forbid(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
+
+int phy_init(struct phy *phy)
+{
+       int ret;
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (phy->init_count++ == 0 && phy->ops->init) {
+               ret = phy->ops->init(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy init failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_init);
+
+int phy_exit(struct phy *phy)
+{
+       int ret;
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (--phy->init_count == 0 && phy->ops->exit) {
+               ret = phy->ops->exit(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy exit failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_exit);
+
+int phy_power_on(struct phy *phy)
+{
+       int ret = -ENOTSUPP;
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (phy->power_count++ == 0 && phy->ops->power_on) {
+               ret = phy->ops->power_on(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&phy->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_power_on);
+
+int phy_power_off(struct phy *phy)
+{
+       int ret = -ENOTSUPP;
+
+       mutex_lock(&phy->mutex);
+       if (--phy->power_count == 0 && phy->ops->power_off) {
+               ret =  phy->ops->power_off(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_power_off);
+
+/**
+ * of_phy_get() - lookup and obtain a reference to a phy by phandle
+ * @dev: device that requests this phy
+ * @index: the index of the phy
+ *
+ * Returns the phy associated with the given phandle value,
+ * after getting a refcount to it or -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * not yet loaded. This function uses of_xlate call back function provided
+ * while registering the phy_provider to find the phy instance.
+ */
+static struct phy *of_phy_get(struct device *dev, int index)
+{
+       int ret;
+       struct phy_provider *phy_provider;
+       struct phy *phy = NULL;
+       struct of_phandle_args args;
+
+       ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells",
+               index, &args);
+       if (ret) {
+               dev_dbg(dev, "failed to get phy in %s node\n",
+                       dev->of_node->full_name);
+               return ERR_PTR(-ENODEV);
+       }
+
+       mutex_lock(&phy_provider_mutex);
+       phy_provider = of_phy_provider_lookup(args.np);
+       if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) {
+               phy = ERR_PTR(-EPROBE_DEFER);
+               goto err0;
+       }
+
+       phy = phy_provider->of_xlate(phy_provider->dev, &args);
+       module_put(phy_provider->owner);
+
+err0:
+       mutex_unlock(&phy_provider_mutex);
+       of_node_put(args.np);
+
+       return phy;
+}
+
+/**
+ * phy_put() - release the PHY
+ * @phy: the phy returned by phy_get()
+ *
+ * Releases a refcount the caller received from phy_get().
+ */
+void phy_put(struct phy *phy)
+{
+       if (IS_ERR(phy))
+               return;
+
+       module_put(phy->ops->owner);
+       put_device(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_put);
+
+/**
+ * devm_phy_put() - release the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_put
+ * to release the phy.
+ */
+void devm_phy_put(struct device *dev, struct phy *phy)
+{
+       int r;
+
+       r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_put);
+
+/**
+ * of_phy_simple_xlate() - returns the phy instance from phy provider
+ * @dev: the PHY provider device
+ * @args: of_phandle_args (not used here)
+ *
+ * Intended to be used by phy provider for the common case where #phy-cells is
+ * 0. For other cases where #phy-cells is greater than '0', the phy provider
+ * should provide a custom of_xlate function that reads the *args* and returns
+ * the appropriate phy.
+ */
+struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
+       *args)
+{
+       struct phy *phy;
+       struct class_dev_iter iter;
+       struct device_node *node = dev->of_node;
+
+       class_dev_iter_init(&iter, phy_class, NULL, NULL);
+       while ((dev = class_dev_iter_next(&iter))) {
+               phy = to_phy(dev);
+               if (node != phy->dev.of_node)
+                       continue;
+
+               class_dev_iter_exit(&iter);
+               return phy;
+       }
+
+       class_dev_iter_exit(&iter);
+       return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(of_phy_simple_xlate);
+
+/**
+ * phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or the name of the controller
+ * port for non-dt case
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy.  The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *phy_get(struct device *dev, const char *string)
+{
+       int index = 0;
+       struct phy *phy = NULL;
+
+       if (string == NULL) {
+               dev_WARN(dev, "missing string\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (dev->of_node) {
+               index = of_property_match_string(dev->of_node, "phy-names",
+                       string);
+               phy = of_phy_get(dev, index);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "unable to find phy\n");
+                       return phy;
+               }
+       } else {
+               phy = phy_lookup(dev, string);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "unable to find phy\n");
+                       return phy;
+               }
+       }
+
+       if (!try_module_get(phy->ops->owner))
+               return ERR_PTR(-EPROBE_DEFER);
+
+       get_device(&phy->dev);
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(phy_get);
+
+/**
+ * devm_phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or phy device name
+ * for non-dt case
+ *
+ * Gets the phy using phy_get(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_get(struct device *dev, const char *string)
+{
+       struct phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy = phy_get(dev, string);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_get);
+
+/**
+ * phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Called to create a phy using phy framework.
+ */
+struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
+       struct phy_init_data *init_data)
+{
+       int ret;
+       int id;
+       struct phy *phy;
+
+       if (!dev) {
+               dev_WARN(dev, "no device provided for PHY\n");
+               ret = -EINVAL;
+               goto err0;
+       }
+
+       phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               ret = -ENOMEM;
+               goto err0;
+       }
+
+       id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
+       if (id < 0) {
+               dev_err(dev, "unable to get id\n");
+               ret = id;
+               goto err0;
+       }
+
+       device_initialize(&phy->dev);
+       mutex_init(&phy->mutex);
+
+       phy->dev.class = phy_class;
+       phy->dev.parent = dev;
+       phy->dev.of_node = dev->of_node;
+       phy->id = id;
+       phy->ops = ops;
+       phy->init_data = init_data;
+
+       ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
+       if (ret)
+               goto err1;
+
+       ret = device_add(&phy->dev);
+       if (ret)
+               goto err1;
+
+       if (pm_runtime_enabled(dev)) {
+               pm_runtime_enable(&phy->dev);
+               pm_runtime_no_callbacks(&phy->dev);
+       }
+
+       return phy;
+
+err1:
+       ida_remove(&phy_ida, phy->id);
+       put_device(&phy->dev);
+       kfree(phy);
+
+err0:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(phy_create);
+
+/**
+ * devm_phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Creates a new PHY device adding it to the PHY class.
+ * While at that, it also associates the device with the phy using devres.
+ * On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
+       struct phy_init_data *init_data)
+{
+       struct phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy = phy_create(dev, ops, init_data);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_create);
+
+/**
+ * phy_destroy() - destroy the phy
+ * @phy: the phy to be destroyed
+ *
+ * Called to destroy the phy.
+ */
+void phy_destroy(struct phy *phy)
+{
+       pm_runtime_disable(&phy->dev);
+       device_unregister(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_destroy);
+
+/**
+ * devm_phy_destroy() - destroy the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_destroy
+ * to destroy the phy.
+ */
+void devm_phy_destroy(struct device *dev, struct phy *phy)
+{
+       int r;
+
+       r = devres_destroy(dev, devm_phy_consume, devm_phy_match, phy);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_destroy);
+
+/**
+ * __of_phy_provider_register() - create/register phy provider with the framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider.
+ */
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       struct phy_provider *phy_provider;
+
+       phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
+       if (!phy_provider)
+               return ERR_PTR(-ENOMEM);
+
+       phy_provider->dev = dev;
+       phy_provider->owner = owner;
+       phy_provider->of_xlate = of_xlate;
+
+       mutex_lock(&phy_provider_mutex);
+       list_add_tail(&phy_provider->list, &phy_provider_list);
+       mutex_unlock(&phy_provider_mutex);
+
+       return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__of_phy_provider_register);
+
+/**
+ * __devm_of_phy_provider_register() - create/register phy provider with the
+ * framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider. While at that, it also associates the device with the
+ * phy provider using devres. On driver detach, release function is invoked
+ * on the devres data, then, devres data is freed.
+ */
+struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       struct phy_provider **ptr, *phy_provider;
+
+       ptr = devres_alloc(devm_phy_provider_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
+       if (!IS_ERR(phy_provider)) {
+               *ptr = phy_provider;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__devm_of_phy_provider_register);
+
+/**
+ * of_phy_provider_unregister() - unregister phy provider from the framework
+ * @phy_provider: phy provider returned by of_phy_provider_register()
+ *
+ * Removes the phy_provider created using of_phy_provider_register().
+ */
+void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+       if (IS_ERR(phy_provider))
+               return;
+
+       mutex_lock(&phy_provider_mutex);
+       list_del(&phy_provider->list);
+       kfree(phy_provider);
+       mutex_unlock(&phy_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(of_phy_provider_unregister);
+
+/**
+ * devm_of_phy_provider_unregister() - remove phy provider from the framework
+ * @dev: struct device of the phy provider
+ *
+ * destroys the devres associated with this phy provider and invokes
+ * of_phy_provider_unregister to unregister the phy provider.
+ */
+void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider) {
+       int r;
+
+       r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match,
+               phy_provider);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY provider device resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_provider_unregister);
+
+/**
+ * phy_release() - release the phy
+ * @dev: the dev member within phy
+ *
+ * When the last reference to the device is removed, it is called
+ * from the embedded kobject as release method.
+ */
+static void phy_release(struct device *dev)
+{
+       struct phy *phy;
+
+       phy = to_phy(dev);
+       dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
+       ida_remove(&phy_ida, phy->id);
+       kfree(phy);
+}
+
+static int __init phy_core_init(void)
+{
+       phy_class = class_create(THIS_MODULE, "phy");
+       if (IS_ERR(phy_class)) {
+               pr_err("failed to create phy class --> %ld\n",
+                       PTR_ERR(phy_class));
+               return PTR_ERR(phy_class);
+       }
+
+       phy_class->dev_release = phy_release;
+
+       return 0;
+}
+module_init(phy_core_init);
+
+static void __exit phy_core_exit(void)
+{
+       class_destroy(phy_class);
+}
+module_exit(phy_core_exit);
+
+MODULE_DESCRIPTION("Generic PHY Framework");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c
new file mode 100644 (file)
index 0000000..1dbe6ce
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Samsung EXYNOS SoC series Display Port PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+/* DPTX_PHY_CONTROL register */
+#define EXYNOS_DPTX_PHY_ENABLE         (1 << 0)
+
+struct exynos_dp_video_phy {
+       void __iomem *regs;
+};
+
+static int __set_phy_state(struct exynos_dp_video_phy *state, unsigned int on)
+{
+       u32 reg;
+
+       reg = readl(state->regs);
+       if (on)
+               reg |= EXYNOS_DPTX_PHY_ENABLE;
+       else
+               reg &= ~EXYNOS_DPTX_PHY_ENABLE;
+       writel(reg, state->regs);
+
+       return 0;
+}
+
+static int exynos_dp_video_phy_power_on(struct phy *phy)
+{
+       struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
+
+       return __set_phy_state(state, 1);
+}
+
+static int exynos_dp_video_phy_power_off(struct phy *phy)
+{
+       struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
+
+       return __set_phy_state(state, 0);
+}
+
+static struct phy_ops exynos_dp_video_phy_ops = {
+       .power_on       = exynos_dp_video_phy_power_on,
+       .power_off      = exynos_dp_video_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int exynos_dp_video_phy_probe(struct platform_device *pdev)
+{
+       struct exynos_dp_video_phy *state;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       struct phy *phy;
+
+       state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       state->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(state->regs))
+               return PTR_ERR(state->regs);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL);
+       if (IS_ERR(phy)) {
+               dev_err(dev, "failed to create Display Port PHY\n");
+               return PTR_ERR(phy);
+       }
+       phy_set_drvdata(phy, state);
+
+       return 0;
+}
+
+static const struct of_device_id exynos_dp_video_phy_of_match[] = {
+       { .compatible = "samsung,exynos5250-dp-video-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match);
+
+static struct platform_driver exynos_dp_video_phy_driver = {
+       .probe  = exynos_dp_video_phy_probe,
+       .driver = {
+               .name   = "exynos-dp-video-phy",
+               .owner  = THIS_MODULE,
+               .of_match_table = exynos_dp_video_phy_of_match,
+       }
+};
+module_platform_driver(exynos_dp_video_phy_driver);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c
new file mode 100644 (file)
index 0000000..0c5efab
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
+#define EXYNOS_MIPI_PHY_CONTROL(n)     ((n) * 4)
+#define EXYNOS_MIPI_PHY_ENABLE         (1 << 0)
+#define EXYNOS_MIPI_PHY_SRESETN                (1 << 1)
+#define EXYNOS_MIPI_PHY_MRESETN                (1 << 2)
+#define EXYNOS_MIPI_PHY_RESET_MASK     (3 << 1)
+
+enum exynos_mipi_phy_id {
+       EXYNOS_MIPI_PHY_ID_CSIS0,
+       EXYNOS_MIPI_PHY_ID_DSIM0,
+       EXYNOS_MIPI_PHY_ID_CSIS1,
+       EXYNOS_MIPI_PHY_ID_DSIM1,
+       EXYNOS_MIPI_PHYS_NUM
+};
+
+#define is_mipi_dsim_phy_id(id) \
+       ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
+
+struct exynos_mipi_video_phy {
+       spinlock_t slock;
+       struct video_phy_desc {
+               struct phy *phy;
+               unsigned int index;
+       } phys[EXYNOS_MIPI_PHYS_NUM];
+       void __iomem *regs;
+};
+
+static int __set_phy_state(struct exynos_mipi_video_phy *state,
+                       enum exynos_mipi_phy_id id, unsigned int on)
+{
+       void __iomem *addr;
+       u32 reg, reset;
+
+       addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+
+       if (is_mipi_dsim_phy_id(id))
+               reset = EXYNOS_MIPI_PHY_MRESETN;
+       else
+               reset = EXYNOS_MIPI_PHY_SRESETN;
+
+       spin_lock(&state->slock);
+       reg = readl(addr);
+       if (on)
+               reg |= reset;
+       else
+               reg &= ~reset;
+       writel(reg, addr);
+
+       /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
+       if (on)
+               reg |= EXYNOS_MIPI_PHY_ENABLE;
+       else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
+               reg &= ~EXYNOS_MIPI_PHY_ENABLE;
+
+       writel(reg, addr);
+       spin_unlock(&state->slock);
+       return 0;
+}
+
+#define to_mipi_video_phy(desc) \
+       container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
+
+static int exynos_mipi_video_phy_power_on(struct phy *phy)
+{
+       struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
+       struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
+
+       return __set_phy_state(state, phy_desc->index, 1);
+}
+
+static int exynos_mipi_video_phy_power_off(struct phy *phy)
+{
+       struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
+       struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
+
+       return __set_phy_state(state, phy_desc->index, 0);
+}
+
+static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
+
+       if (WARN_ON(args->args[0] > EXYNOS_MIPI_PHYS_NUM))
+               return ERR_PTR(-ENODEV);
+
+       return state->phys[args->args[0]].phy;
+}
+
+static struct phy_ops exynos_mipi_video_phy_ops = {
+       .power_on       = exynos_mipi_video_phy_power_on,
+       .power_off      = exynos_mipi_video_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
+{
+       struct exynos_mipi_video_phy *state;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       unsigned int i;
+
+       state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       state->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(state->regs))
+               return PTR_ERR(state->regs);
+
+       dev_set_drvdata(dev, state);
+       spin_lock_init(&state->slock);
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                       exynos_mipi_video_phy_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
+               struct phy *phy = devm_phy_create(dev,
+                                       &exynos_mipi_video_phy_ops, NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "failed to create PHY %d\n", i);
+                       return PTR_ERR(phy);
+               }
+
+               state->phys[i].phy = phy;
+               state->phys[i].index = i;
+               phy_set_drvdata(phy, &state->phys[i]);
+       }
+
+       return 0;
+}
+
+static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
+       { .compatible = "samsung,s5pv210-mipi-video-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
+
+static struct platform_driver exynos_mipi_video_phy_driver = {
+       .probe  = exynos_mipi_video_phy_probe,
+       .driver = {
+               .of_match_table = exynos_mipi_video_phy_of_match,
+               .name  = "exynos-mipi-video-phy",
+               .owner = THIS_MODULE,
+       }
+};
+module_platform_driver(exynos_mipi_video_phy_driver);
+
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
new file mode 100644 (file)
index 0000000..bfc5c33
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * omap-usb2.c - USB PHY, talking to musb controller in OMAP.
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/usb/omap_usb.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/usb/omap_control_usb.h>
+#include <linux/phy/phy.h>
+#include <linux/of_platform.h>
+
+/**
+ * omap_usb2_set_comparator - links the comparator present in the sytem with
+ *     this phy
+ * @comparator - the companion phy(comparator) for this phy
+ *
+ * The phy companion driver should call this API passing the phy_companion
+ * filled with set_vbus and start_srp to be used by usb phy.
+ *
+ * For use by phy companion driver
+ */
+int omap_usb2_set_comparator(struct phy_companion *comparator)
+{
+       struct omap_usb *phy;
+       struct usb_phy  *x = usb_get_phy(USB_PHY_TYPE_USB2);
+
+       if (IS_ERR(x))
+               return -ENODEV;
+
+       phy = phy_to_omapusb(x);
+       phy->comparator = comparator;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
+
+static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
+{
+       struct omap_usb *phy = phy_to_omapusb(otg->phy);
+
+       if (!phy->comparator)
+               return -ENODEV;
+
+       return phy->comparator->set_vbus(phy->comparator, enabled);
+}
+
+static int omap_usb_start_srp(struct usb_otg *otg)
+{
+       struct omap_usb *phy = phy_to_omapusb(otg->phy);
+
+       if (!phy->comparator)
+               return -ENODEV;
+
+       return phy->comparator->start_srp(phy->comparator);
+}
+
+static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+       struct usb_phy  *phy = otg->phy;
+
+       otg->host = host;
+       if (!host)
+               phy->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static int omap_usb_set_peripheral(struct usb_otg *otg,
+               struct usb_gadget *gadget)
+{
+       struct usb_phy  *phy = otg->phy;
+
+       otg->gadget = gadget;
+       if (!gadget)
+               phy->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static int omap_usb2_suspend(struct usb_phy *x, int suspend)
+{
+       struct omap_usb *phy = phy_to_omapusb(x);
+       int ret;
+
+       if (suspend && !phy->is_suspended) {
+               omap_control_usb_phy_power(phy->control_dev, 0);
+               pm_runtime_put_sync(phy->dev);
+               phy->is_suspended = 1;
+       } else if (!suspend && phy->is_suspended) {
+               ret = pm_runtime_get_sync(phy->dev);
+               if (ret < 0) {
+                       dev_err(phy->dev, "get_sync failed with err %d\n", ret);
+                       return ret;
+               }
+               omap_control_usb_phy_power(phy->control_dev, 1);
+               phy->is_suspended = 0;
+       }
+
+       return 0;
+}
+
+static int omap_usb_power_off(struct phy *x)
+{
+       struct omap_usb *phy = phy_get_drvdata(x);
+
+       omap_control_usb_phy_power(phy->control_dev, 0);
+
+       return 0;
+}
+
+static int omap_usb_power_on(struct phy *x)
+{
+       struct omap_usb *phy = phy_get_drvdata(x);
+
+       omap_control_usb_phy_power(phy->control_dev, 1);
+
+       return 0;
+}
+
+static struct phy_ops ops = {
+       .power_on       = omap_usb_power_on,
+       .power_off      = omap_usb_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int omap_usb2_probe(struct platform_device *pdev)
+{
+       struct omap_usb *phy;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       struct usb_otg *otg;
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *control_node;
+       struct platform_device *control_pdev;
+
+       if (!node)
+               return -EINVAL;
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
+               return -ENOMEM;
+       }
+
+       otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+       if (!otg) {
+               dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n");
+               return -ENOMEM;
+       }
+
+       phy->dev                = &pdev->dev;
+
+       phy->phy.dev            = phy->dev;
+       phy->phy.label          = "omap-usb2";
+       phy->phy.set_suspend    = omap_usb2_suspend;
+       phy->phy.otg            = otg;
+       phy->phy.type           = USB_PHY_TYPE_USB2;
+
+       phy_provider = devm_of_phy_provider_register(phy->dev,
+                       of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       control_node = of_parse_phandle(node, "ctrl-module", 0);
+       if (!control_node) {
+               dev_err(&pdev->dev, "Failed to get control device phandle\n");
+               return -EINVAL;
+       }
+
+       control_pdev = of_find_device_by_node(control_node);
+       if (!control_pdev) {
+               dev_err(&pdev->dev, "Failed to get control device\n");
+               return -EINVAL;
+       }
+
+       phy->control_dev = &control_pdev->dev;
+
+       phy->is_suspended       = 1;
+       omap_control_usb_phy_power(phy->control_dev, 0);
+
+       otg->set_host           = omap_usb_set_host;
+       otg->set_peripheral     = omap_usb_set_peripheral;
+       otg->set_vbus           = omap_usb_set_vbus;
+       otg->start_srp          = omap_usb_start_srp;
+       otg->phy                = &phy->phy;
+
+       platform_set_drvdata(pdev, phy);
+       pm_runtime_enable(phy->dev);
+
+       generic_phy = devm_phy_create(phy->dev, &ops, NULL);
+       if (IS_ERR(generic_phy))
+               return PTR_ERR(generic_phy);
+
+       phy_set_drvdata(generic_phy, phy);
+
+       phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
+       if (IS_ERR(phy->wkupclk)) {
+               dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
+               return PTR_ERR(phy->wkupclk);
+       }
+       clk_prepare(phy->wkupclk);
+
+       phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
+       if (IS_ERR(phy->optclk))
+               dev_vdbg(&pdev->dev, "unable to get refclk960m\n");
+       else
+               clk_prepare(phy->optclk);
+
+       usb_add_phy_dev(&phy->phy);
+
+       return 0;
+}
+
+static int omap_usb2_remove(struct platform_device *pdev)
+{
+       struct omap_usb *phy = platform_get_drvdata(pdev);
+
+       clk_unprepare(phy->wkupclk);
+       if (!IS_ERR(phy->optclk))
+               clk_unprepare(phy->optclk);
+       usb_remove_phy(&phy->phy);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int omap_usb2_runtime_suspend(struct device *dev)
+{
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct omap_usb *phy = platform_get_drvdata(pdev);
+
+       clk_disable(phy->wkupclk);
+       if (!IS_ERR(phy->optclk))
+               clk_disable(phy->optclk);
+
+       return 0;
+}
+
+static int omap_usb2_runtime_resume(struct device *dev)
+{
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct omap_usb *phy = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = clk_enable(phy->wkupclk);
+       if (ret < 0) {
+               dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
+               goto err0;
+       }
+
+       if (!IS_ERR(phy->optclk)) {
+               ret = clk_enable(phy->optclk);
+               if (ret < 0) {
+                       dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
+                       goto err1;
+               }
+       }
+
+       return 0;
+
+err1:
+       clk_disable(phy->wkupclk);
+
+err0:
+       return ret;
+}
+
+static const struct dev_pm_ops omap_usb2_pm_ops = {
+       SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume,
+               NULL)
+};
+
+#define DEV_PM_OPS     (&omap_usb2_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_usb2_id_table[] = {
+       { .compatible = "ti,omap-usb2" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
+#endif
+
+static struct platform_driver omap_usb2_driver = {
+       .probe          = omap_usb2_probe,
+       .remove         = omap_usb2_remove,
+       .driver         = {
+               .name   = "omap-usb2",
+               .owner  = THIS_MODULE,
+               .pm     = DEV_PM_OPS,
+               .of_match_table = of_match_ptr(omap_usb2_id_table),
+       },
+};
+
+module_platform_driver(omap_usb2_driver);
+
+MODULE_ALIAS("platform: omap_usb2");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP USB2 phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
new file mode 100644 (file)
index 0000000..daf65e6
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+ * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller
+ *
+ * Copyright (C) 2004-2007 Texas Instruments
+ * Copyright (C) 2008 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Current status:
+ *     - HS USB ULPI mode works.
+ *     - 3-pin mode support may be added in future.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
+#include <linux/usb/musb-omap.h>
+#include <linux/usb/ulpi.h>
+#include <linux/i2c/twl.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+/* Register defines */
+
+#define MCPC_CTRL                      0x30
+#define MCPC_CTRL_RTSOL                        (1 << 7)
+#define MCPC_CTRL_EXTSWR               (1 << 6)
+#define MCPC_CTRL_EXTSWC               (1 << 5)
+#define MCPC_CTRL_VOICESW              (1 << 4)
+#define MCPC_CTRL_OUT64K               (1 << 3)
+#define MCPC_CTRL_RTSCTSSW             (1 << 2)
+#define MCPC_CTRL_HS_UART              (1 << 0)
+
+#define MCPC_IO_CTRL                   0x33
+#define MCPC_IO_CTRL_MICBIASEN         (1 << 5)
+#define MCPC_IO_CTRL_CTS_NPU           (1 << 4)
+#define MCPC_IO_CTRL_RXD_PU            (1 << 3)
+#define MCPC_IO_CTRL_TXDTYP            (1 << 2)
+#define MCPC_IO_CTRL_CTSTYP            (1 << 1)
+#define MCPC_IO_CTRL_RTSTYP            (1 << 0)
+
+#define MCPC_CTRL2                     0x36
+#define MCPC_CTRL2_MCPC_CK_EN          (1 << 0)
+
+#define OTHER_FUNC_CTRL                        0x80
+#define OTHER_FUNC_CTRL_BDIS_ACON_EN   (1 << 4)
+#define OTHER_FUNC_CTRL_FIVEWIRE_MODE  (1 << 2)
+
+#define OTHER_IFC_CTRL                 0x83
+#define OTHER_IFC_CTRL_OE_INT_EN       (1 << 6)
+#define OTHER_IFC_CTRL_CEA2011_MODE    (1 << 5)
+#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN     (1 << 4)
+#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT      (1 << 3)
+#define OTHER_IFC_CTRL_HIZ_ULPI                (1 << 2)
+#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0)
+
+#define OTHER_INT_EN_RISE              0x86
+#define OTHER_INT_EN_FALL              0x89
+#define OTHER_INT_STS                  0x8C
+#define OTHER_INT_LATCH                        0x8D
+#define OTHER_INT_VB_SESS_VLD          (1 << 7)
+#define OTHER_INT_DM_HI                        (1 << 6) /* not valid for "latch" reg */
+#define OTHER_INT_DP_HI                        (1 << 5) /* not valid for "latch" reg */
+#define OTHER_INT_BDIS_ACON            (1 << 3) /* not valid for "fall" regs */
+#define OTHER_INT_MANU                 (1 << 1)
+#define OTHER_INT_ABNORMAL_STRESS      (1 << 0)
+
+#define ID_STATUS                      0x96
+#define ID_RES_FLOAT                   (1 << 4)
+#define ID_RES_440K                    (1 << 3)
+#define ID_RES_200K                    (1 << 2)
+#define ID_RES_102K                    (1 << 1)
+#define ID_RES_GND                     (1 << 0)
+
+#define POWER_CTRL                     0xAC
+#define POWER_CTRL_OTG_ENAB            (1 << 5)
+
+#define OTHER_IFC_CTRL2                        0xAF
+#define OTHER_IFC_CTRL2_ULPI_STP_LOW   (1 << 4)
+#define OTHER_IFC_CTRL2_ULPI_TXEN_POL  (1 << 3)
+#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2)
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK    (3 << 0) /* bits 0 and 1 */
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N   (0 << 0)
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N   (1 << 0)
+
+#define REG_CTRL_EN                    0xB2
+#define REG_CTRL_ERROR                 0xB5
+#define ULPI_I2C_CONFLICT_INTEN                (1 << 0)
+
+#define OTHER_FUNC_CTRL2               0xB8
+#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0)
+
+/* following registers do not have separate _clr and _set registers */
+#define VBUS_DEBOUNCE                  0xC0
+#define ID_DEBOUNCE                    0xC1
+#define VBAT_TIMER                     0xD3
+#define PHY_PWR_CTRL                   0xFD
+#define PHY_PWR_PHYPWD                 (1 << 0)
+#define PHY_CLK_CTRL                   0xFE
+#define PHY_CLK_CTRL_CLOCKGATING_EN    (1 << 2)
+#define PHY_CLK_CTRL_CLK32K_EN         (1 << 1)
+#define REQ_PHY_DPLL_CLK               (1 << 0)
+#define PHY_CLK_CTRL_STS               0xFF
+#define PHY_DPLL_CLK                   (1 << 0)
+
+/* In module TWL_MODULE_PM_MASTER */
+#define STS_HW_CONDITIONS              0x0F
+
+/* In module TWL_MODULE_PM_RECEIVER */
+#define VUSB_DEDICATED1                        0x7D
+#define VUSB_DEDICATED2                        0x7E
+#define VUSB1V5_DEV_GRP                        0x71
+#define VUSB1V5_TYPE                   0x72
+#define VUSB1V5_REMAP                  0x73
+#define VUSB1V8_DEV_GRP                        0x74
+#define VUSB1V8_TYPE                   0x75
+#define VUSB1V8_REMAP                  0x76
+#define VUSB3V1_DEV_GRP                        0x77
+#define VUSB3V1_TYPE                   0x78
+#define VUSB3V1_REMAP                  0x79
+
+/* In module TWL4030_MODULE_INTBR */
+#define PMBR1                          0x0D
+#define GPIO_USB_4PIN_ULPI_2430C       (3 << 0)
+
+struct twl4030_usb {
+       struct usb_phy          phy;
+       struct device           *dev;
+
+       /* TWL4030 internal USB regulator supplies */
+       struct regulator        *usb1v5;
+       struct regulator        *usb1v8;
+       struct regulator        *usb3v1;
+
+       /* for vbus reporting with irqs disabled */
+       spinlock_t              lock;
+
+       /* pin configuration */
+       enum twl4030_usb_mode   usb_mode;
+
+       int                     irq;
+       enum omap_musb_vbus_id_status linkstat;
+       bool                    vbus_supplied;
+       u8                      asleep;
+       bool                    irq_enabled;
+
+       struct delayed_work     id_workaround_work;
+};
+
+/* internal define on top of container_of */
+#define phy_to_twl(x)          container_of((x), struct twl4030_usb, phy)
+
+/*-------------------------------------------------------------------------*/
+
+static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl,
+               u8 module, u8 data, u8 address)
+{
+       u8 check;
+
+       if ((twl_i2c_write_u8(module, data, address) >= 0) &&
+           (twl_i2c_read_u8(module, &check, address) >= 0) &&
+                                               (check == data))
+               return 0;
+       dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
+                       1, module, address, check, data);
+
+       /* Failed once: Try again */
+       if ((twl_i2c_write_u8(module, data, address) >= 0) &&
+           (twl_i2c_read_u8(module, &check, address) >= 0) &&
+                                               (check == data))
+               return 0;
+       dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
+                       2, module, address, check, data);
+
+       /* Failed again: Return error */
+       return -EBUSY;
+}
+
+#define twl4030_usb_write_verify(twl, address, data)   \
+       twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address))
+
+static inline int twl4030_usb_write(struct twl4030_usb *twl,
+               u8 address, u8 data)
+{
+       int ret = 0;
+
+       ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address);
+       if (ret < 0)
+               dev_dbg(twl->dev,
+                       "TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
+       return ret;
+}
+
+static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address)
+{
+       u8 data;
+       int ret = 0;
+
+       ret = twl_i2c_read_u8(module, &data, address);
+       if (ret >= 0)
+               ret = data;
+       else
+               dev_dbg(twl->dev,
+                       "TWL4030:readb[0x%x,0x%x] Error %d\n",
+                                       module, address, ret);
+
+       return ret;
+}
+
+static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address)
+{
+       return twl4030_readb(twl, TWL_MODULE_USB, address);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline int
+twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
+{
+       return twl4030_usb_write(twl, ULPI_SET(reg), bits);
+}
+
+static inline int
+twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
+{
+       return twl4030_usb_write(twl, ULPI_CLR(reg), bits);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static bool twl4030_is_driving_vbus(struct twl4030_usb *twl)
+{
+       int ret;
+
+       ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS);
+       if (ret < 0 || !(ret & PHY_DPLL_CLK))
+               /*
+                * if clocks are off, registers are not updated,
+                * but we can assume we don't drive VBUS in this case
+                */
+               return false;
+
+       ret = twl4030_usb_read(twl, ULPI_OTG_CTRL);
+       if (ret < 0)
+               return false;
+
+       return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false;
+}
+
+static enum omap_musb_vbus_id_status
+       twl4030_usb_linkstat(struct twl4030_usb *twl)
+{
+       int     status;
+       enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
+
+       twl->vbus_supplied = false;
+
+       /*
+        * For ID/VBUS sensing, see manual section 15.4.8 ...
+        * except when using only battery backup power, two
+        * comparators produce VBUS_PRES and ID_PRES signals,
+        * which don't match docs elsewhere.  But ... BIT(7)
+        * and BIT(2) of STS_HW_CONDITIONS, respectively, do
+        * seem to match up.  If either is true the USB_PRES
+        * signal is active, the OTG module is activated, and
+        * its interrupt may be raised (may wake the system).
+        */
+       status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS);
+       if (status < 0)
+               dev_err(twl->dev, "USB link status err %d\n", status);
+       else if (status & (BIT(7) | BIT(2))) {
+               if (status & BIT(7)) {
+                       if (twl4030_is_driving_vbus(twl))
+                               status &= ~BIT(7);
+                       else
+                               twl->vbus_supplied = true;
+               }
+
+               if (status & BIT(2))
+                       linkstat = OMAP_MUSB_ID_GROUND;
+               else if (status & BIT(7))
+                       linkstat = OMAP_MUSB_VBUS_VALID;
+               else
+                       linkstat = OMAP_MUSB_VBUS_OFF;
+       } else {
+               if (twl->linkstat != OMAP_MUSB_UNKNOWN)
+                       linkstat = OMAP_MUSB_VBUS_OFF;
+       }
+
+       dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
+                       status, status, linkstat);
+
+       /* REVISIT this assumes host and peripheral controllers
+        * are registered, and that both are active...
+        */
+
+       return linkstat;
+}
+
+static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
+{
+       twl->usb_mode = mode;
+
+       switch (mode) {
+       case T2_USB_MODE_ULPI:
+               twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL,
+                                       ULPI_IFC_CTRL_CARKITMODE);
+               twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+               twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL,
+                                       ULPI_FUNC_CTRL_XCVRSEL_MASK |
+                                       ULPI_FUNC_CTRL_OPMODE_MASK);
+               break;
+       case -1:
+               /* FIXME: power on defaults */
+               break;
+       default:
+               dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
+                               mode);
+               break;
+       };
+}
+
+static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
+{
+       unsigned long timeout;
+       int val = twl4030_usb_read(twl, PHY_CLK_CTRL);
+
+       if (val >= 0) {
+               if (on) {
+                       /* enable DPLL to access PHY registers over I2C */
+                       val |= REQ_PHY_DPLL_CLK;
+                       WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
+                                               (u8)val) < 0);
+
+                       timeout = jiffies + HZ;
+                       while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
+                                                       PHY_DPLL_CLK)
+                               && time_before(jiffies, timeout))
+                                       udelay(10);
+                       if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
+                                                       PHY_DPLL_CLK))
+                               dev_err(twl->dev, "Timeout setting T2 HSUSB "
+                                               "PHY DPLL clock\n");
+               } else {
+                       /* let ULPI control the DPLL clock */
+                       val &= ~REQ_PHY_DPLL_CLK;
+                       WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
+                                               (u8)val) < 0);
+               }
+       }
+}
+
+static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
+{
+       u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
+
+       if (on)
+               pwr &= ~PHY_PWR_PHYPWD;
+       else
+               pwr |= PHY_PWR_PHYPWD;
+
+       WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
+}
+
+static void twl4030_phy_power(struct twl4030_usb *twl, int on)
+{
+       int ret;
+
+       if (on) {
+               ret = regulator_enable(twl->usb3v1);
+               if (ret)
+                       dev_err(twl->dev, "Failed to enable usb3v1\n");
+
+               ret = regulator_enable(twl->usb1v8);
+               if (ret)
+                       dev_err(twl->dev, "Failed to enable usb1v8\n");
+
+               /*
+                * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP
+                * in twl4030) resets the VUSB_DEDICATED2 register. This reset
+                * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to
+                * SLEEP. We work around this by clearing the bit after usv3v1
+                * is re-activated. This ensures that VUSB3V1 is really active.
+                */
+               twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
+
+               ret = regulator_enable(twl->usb1v5);
+               if (ret)
+                       dev_err(twl->dev, "Failed to enable usb1v5\n");
+
+               __twl4030_phy_power(twl, 1);
+               twl4030_usb_write(twl, PHY_CLK_CTRL,
+                                 twl4030_usb_read(twl, PHY_CLK_CTRL) |
+                                       (PHY_CLK_CTRL_CLOCKGATING_EN |
+                                               PHY_CLK_CTRL_CLK32K_EN));
+       } else {
+               __twl4030_phy_power(twl, 0);
+               regulator_disable(twl->usb1v5);
+               regulator_disable(twl->usb1v8);
+               regulator_disable(twl->usb3v1);
+       }
+}
+
+static int twl4030_phy_power_off(struct phy *phy)
+{
+       struct twl4030_usb *twl = phy_get_drvdata(phy);
+
+       if (twl->asleep)
+               return 0;
+
+       twl4030_phy_power(twl, 0);
+       twl->asleep = 1;
+       dev_dbg(twl->dev, "%s\n", __func__);
+       return 0;
+}
+
+static void __twl4030_phy_power_on(struct twl4030_usb *twl)
+{
+       twl4030_phy_power(twl, 1);
+       twl4030_i2c_access(twl, 1);
+       twl4030_usb_set_mode(twl, twl->usb_mode);
+       if (twl->usb_mode == T2_USB_MODE_ULPI)
+               twl4030_i2c_access(twl, 0);
+}
+
+static int twl4030_phy_power_on(struct phy *phy)
+{
+       struct twl4030_usb *twl = phy_get_drvdata(phy);
+
+       if (!twl->asleep)
+               return 0;
+       __twl4030_phy_power_on(twl);
+       twl->asleep = 0;
+       dev_dbg(twl->dev, "%s\n", __func__);
+
+       /*
+        * XXX When VBUS gets driven after musb goes to A mode,
+        * ID_PRES related interrupts no longer arrive, why?
+        * Register itself is updated fine though, so we must poll.
+        */
+       if (twl->linkstat == OMAP_MUSB_ID_GROUND) {
+               cancel_delayed_work(&twl->id_workaround_work);
+               schedule_delayed_work(&twl->id_workaround_work, HZ);
+       }
+       return 0;
+}
+
+static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
+{
+       /* Enable writing to power configuration registers */
+       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
+                        TWL4030_PM_MASTER_PROTECT_KEY);
+
+       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2,
+                        TWL4030_PM_MASTER_PROTECT_KEY);
+
+       /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/
+       /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/
+
+       /* input to VUSB3V1 LDO is from VBAT, not VBUS */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
+
+       /* Initialize 3.1V regulator */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
+
+       twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1");
+       if (IS_ERR(twl->usb3v1))
+               return -ENODEV;
+
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
+
+       /* Initialize 1.5V regulator */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
+
+       twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5");
+       if (IS_ERR(twl->usb1v5))
+               return -ENODEV;
+
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
+
+       /* Initialize 1.8V regulator */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
+
+       twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8");
+       if (IS_ERR(twl->usb1v8))
+               return -ENODEV;
+
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
+
+       /* disable access to power configuration registers */
+       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
+                        TWL4030_PM_MASTER_PROTECT_KEY);
+
+       return 0;
+}
+
+static ssize_t twl4030_usb_vbus_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct twl4030_usb *twl = dev_get_drvdata(dev);
+       unsigned long flags;
+       int ret = -EINVAL;
+
+       spin_lock_irqsave(&twl->lock, flags);
+       ret = sprintf(buf, "%s\n",
+                       twl->vbus_supplied ? "on" : "off");
+       spin_unlock_irqrestore(&twl->lock, flags);
+
+       return ret;
+}
+static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
+
+static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
+{
+       struct twl4030_usb *twl = _twl;
+       enum omap_musb_vbus_id_status status;
+       bool status_changed = false;
+
+       status = twl4030_usb_linkstat(twl);
+
+       spin_lock_irq(&twl->lock);
+       if (status >= 0 && status != twl->linkstat) {
+               twl->linkstat = status;
+               status_changed = true;
+       }
+       spin_unlock_irq(&twl->lock);
+
+       if (status_changed) {
+               /* FIXME add a set_power() method so that B-devices can
+                * configure the charger appropriately.  It's not always
+                * correct to consume VBUS power, and how much current to
+                * consume is a function of the USB configuration chosen
+                * by the host.
+                *
+                * REVISIT usb_gadget_vbus_connect(...) as needed, ditto
+                * its disconnect() sibling, when changing to/from the
+                * USB_LINK_VBUS state.  musb_hdrc won't care until it
+                * starts to handle softconnect right.
+                */
+               omap_musb_mailbox(status);
+       }
+       sysfs_notify(&twl->dev->kobj, NULL, "vbus");
+
+       return IRQ_HANDLED;
+}
+
+static void twl4030_id_workaround_work(struct work_struct *work)
+{
+       struct twl4030_usb *twl = container_of(work, struct twl4030_usb,
+               id_workaround_work.work);
+       enum omap_musb_vbus_id_status status;
+       bool status_changed = false;
+
+       status = twl4030_usb_linkstat(twl);
+
+       spin_lock_irq(&twl->lock);
+       if (status >= 0 && status != twl->linkstat) {
+               twl->linkstat = status;
+               status_changed = true;
+       }
+       spin_unlock_irq(&twl->lock);
+
+       if (status_changed) {
+               dev_dbg(twl->dev, "handle missing status change to %d\n",
+                               status);
+               omap_musb_mailbox(status);
+       }
+
+       /* don't schedule during sleep - irq works right then */
+       if (status == OMAP_MUSB_ID_GROUND && !twl->asleep) {
+               cancel_delayed_work(&twl->id_workaround_work);
+               schedule_delayed_work(&twl->id_workaround_work, HZ);
+       }
+}
+
+static int twl4030_phy_init(struct phy *phy)
+{
+       struct twl4030_usb *twl = phy_get_drvdata(phy);
+       enum omap_musb_vbus_id_status status;
+
+       /*
+        * Start in sleep state, we'll get called through set_suspend()
+        * callback when musb is runtime resumed and it's time to start.
+        */
+       __twl4030_phy_power(twl, 0);
+       twl->asleep = 1;
+
+       status = twl4030_usb_linkstat(twl);
+       twl->linkstat = status;
+
+       if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) {
+               omap_musb_mailbox(twl->linkstat);
+               twl4030_phy_power_on(phy);
+       }
+
+       sysfs_notify(&twl->dev->kobj, NULL, "vbus");
+       return 0;
+}
+
+static int twl4030_set_peripheral(struct usb_otg *otg,
+                                       struct usb_gadget *gadget)
+{
+       if (!otg)
+               return -ENODEV;
+
+       otg->gadget = gadget;
+       if (!gadget)
+               otg->phy->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+       if (!otg)
+               return -ENODEV;
+
+       otg->host = host;
+       if (!host)
+               otg->phy->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static const struct phy_ops ops = {
+       .init           = twl4030_phy_init,
+       .power_on       = twl4030_phy_power_on,
+       .power_off      = twl4030_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int twl4030_usb_probe(struct platform_device *pdev)
+{
+       struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev);
+       struct twl4030_usb      *twl;
+       struct phy              *phy;
+       int                     status, err;
+       struct usb_otg          *otg;
+       struct device_node      *np = pdev->dev.of_node;
+       struct phy_provider     *phy_provider;
+       struct phy_init_data    *init_data = NULL;
+
+       twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
+       if (!twl)
+               return -ENOMEM;
+
+       if (np)
+               of_property_read_u32(np, "usb_mode",
+                               (enum twl4030_usb_mode *)&twl->usb_mode);
+       else if (pdata) {
+               twl->usb_mode = pdata->usb_mode;
+               init_data = pdata->init_data;
+       } else {
+               dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
+               return -EINVAL;
+       }
+
+       otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
+       if (!otg)
+               return -ENOMEM;
+
+       twl->dev                = &pdev->dev;
+       twl->irq                = platform_get_irq(pdev, 0);
+       twl->vbus_supplied      = false;
+       twl->asleep             = 1;
+       twl->linkstat           = OMAP_MUSB_UNKNOWN;
+
+       twl->phy.dev            = twl->dev;
+       twl->phy.label          = "twl4030";
+       twl->phy.otg            = otg;
+       twl->phy.type           = USB_PHY_TYPE_USB2;
+
+       otg->phy                = &twl->phy;
+       otg->set_host           = twl4030_set_host;
+       otg->set_peripheral     = twl4030_set_peripheral;
+
+       phy_provider = devm_of_phy_provider_register(twl->dev,
+               of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       phy = devm_phy_create(twl->dev, &ops, init_data);
+       if (IS_ERR(phy)) {
+               dev_dbg(&pdev->dev, "Failed to create PHY\n");
+               return PTR_ERR(phy);
+       }
+
+       phy_set_drvdata(phy, twl);
+
+       /* init spinlock for workqueue */
+       spin_lock_init(&twl->lock);
+
+       INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work);
+
+       err = twl4030_usb_ldo_init(twl);
+       if (err) {
+               dev_err(&pdev->dev, "ldo init failed\n");
+               return err;
+       }
+       usb_add_phy_dev(&twl->phy);
+
+       platform_set_drvdata(pdev, twl);
+       if (device_create_file(&pdev->dev, &dev_attr_vbus))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+
+       ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
+
+       /* Our job is to use irqs and status from the power module
+        * to keep the transceiver disabled when nothing's connected.
+        *
+        * FIXME we actually shouldn't start enabling it until the
+        * USB controller drivers have said they're ready, by calling
+        * set_host() and/or set_peripheral() ... OTG_capable boards
+        * need both handles, otherwise just one suffices.
+        */
+       twl->irq_enabled = true;
+       status = devm_request_threaded_irq(twl->dev, twl->irq, NULL,
+                       twl4030_usb_irq, IRQF_TRIGGER_FALLING |
+                       IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl);
+       if (status < 0) {
+               dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
+                       twl->irq, status);
+               return status;
+       }
+
+       dev_info(&pdev->dev, "Initialized TWL4030 USB module\n");
+       return 0;
+}
+
+static int twl4030_usb_remove(struct platform_device *pdev)
+{
+       struct twl4030_usb *twl = platform_get_drvdata(pdev);
+       int val;
+
+       cancel_delayed_work(&twl->id_workaround_work);
+       device_remove_file(twl->dev, &dev_attr_vbus);
+
+       /* set transceiver mode to power on defaults */
+       twl4030_usb_set_mode(twl, -1);
+
+       /* autogate 60MHz ULPI clock,
+        * clear dpll clock request for i2c access,
+        * disable 32KHz
+        */
+       val = twl4030_usb_read(twl, PHY_CLK_CTRL);
+       if (val >= 0) {
+               val |= PHY_CLK_CTRL_CLOCKGATING_EN;
+               val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK);
+               twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val);
+       }
+
+       /* disable complete OTG block */
+       twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+
+       if (!twl->asleep)
+               twl4030_phy_power(twl, 0);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl4030_usb_id_table[] = {
+       { .compatible = "ti,twl4030-usb" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, twl4030_usb_id_table);
+#endif
+
+static struct platform_driver twl4030_usb_driver = {
+       .probe          = twl4030_usb_probe,
+       .remove         = twl4030_usb_remove,
+       .driver         = {
+               .name   = "twl4030_usb",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(twl4030_usb_id_table),
+       },
+};
+
+static int __init twl4030_usb_init(void)
+{
+       return platform_driver_register(&twl4030_usb_driver);
+}
+subsys_initcall(twl4030_usb_init);
+
+static void __exit twl4030_usb_exit(void)
+{
+       platform_driver_unregister(&twl4030_usb_driver);
+}
+module_exit(twl4030_usb_exit);
+
+MODULE_ALIAS("platform:twl4030_usb");
+MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation");
+MODULE_DESCRIPTION("TWL4030 USB transceiver driver");
+MODULE_LICENSE("GPL");
index 96d6b2eef4f2a1209f2c9f113e3932b8eb812738..b51a7460cc49bc03b4c055e8f46bfe2fedf718ff 100644 (file)
@@ -504,6 +504,7 @@ config ASUS_WMI
        depends on BACKLIGHT_CLASS_DEVICE
        depends on RFKILL || RFKILL = n
        depends on HOTPLUG_PCI
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        select INPUT_SPARSEKMAP
        select LEDS_CLASS
        select NEW_LEDS
index d3fd52036fd6e041592c7060c987ed5508684a0e..13ec195f0ca65f6e158e872b6a60ba3b8d04ddc1 100644 (file)
@@ -127,18 +127,17 @@ MODULE_PARM_DESC(minor,
                 "default is -1 (automatic)");
 #endif
 
-static int kbd_backlight = 1;
+static int kbd_backlight = -1;
 module_param(kbd_backlight, int, 0444);
 MODULE_PARM_DESC(kbd_backlight,
                 "set this to 0 to disable keyboard backlight, "
-                "1 to enable it (default: 0)");
+                "1 to enable it (default: no change from current value)");
 
-static int kbd_backlight_timeout;      /* = 0 */
+static int kbd_backlight_timeout = -1;
 module_param(kbd_backlight_timeout, int, 0444);
 MODULE_PARM_DESC(kbd_backlight_timeout,
-                "set this to 0 to set the default 10 seconds timeout, "
-                "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
-                "(default: 0)");
+                "meaningful values vary from 0 to 3 and their meaning depends "
+                "on the model (default: no change from current value)");
 
 #ifdef CONFIG_PM_SLEEP
 static void sony_nc_kbd_backlight_resume(void);
@@ -1844,6 +1843,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
        if (!kbdbl_ctl)
                return -ENOMEM;
 
+       kbdbl_ctl->mode = kbd_backlight;
+       kbdbl_ctl->timeout = kbd_backlight_timeout;
        kbdbl_ctl->handle = handle;
        if (handle == 0x0137)
                kbdbl_ctl->base = 0x0C00;
@@ -1870,8 +1871,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
        if (ret)
                goto outmode;
 
-       __sony_nc_kbd_backlight_mode_set(kbd_backlight);
-       __sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout);
+       __sony_nc_kbd_backlight_mode_set(kbdbl_ctl->mode);
+       __sony_nc_kbd_backlight_timeout_set(kbdbl_ctl->timeout);
 
        return 0;
 
@@ -1886,17 +1887,8 @@ outkzalloc:
 static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
 {
        if (kbdbl_ctl) {
-               int result;
-
                device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
                device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
-
-               /* restore the default hw behaviour */
-               sony_call_snc_handle(kbdbl_ctl->handle,
-                               kbdbl_ctl->base | 0x10000, &result);
-               sony_call_snc_handle(kbdbl_ctl->handle,
-                               kbdbl_ctl->base + 0x200, &result);
-
                kfree(kbdbl_ctl);
                kbdbl_ctl = NULL;
        }
index ffd53e3eb92f1191116c84f5e85a3723988879f5..c8873b0ca551d9fc93add48ae212e7bbf3ef7662 100644 (file)
@@ -4,7 +4,7 @@
  */
 
 extern spinlock_t pnp_lock;
-extern struct device_attribute pnp_interface_attrs[];
+extern const struct attribute_group *pnp_dev_groups[];
 void *pnp_alloc(long size);
 
 int pnp_register_protocol(struct pnp_protocol *protocol);
index a39ee38a9414cece2b0742e05f2cf5260a0fc842..6936e0acedcd6bfce4b8166bb4b8da6a922e0f47 100644 (file)
@@ -246,7 +246,7 @@ struct bus_type pnp_bus_type = {
        .remove  = pnp_device_remove,
        .shutdown = pnp_device_shutdown,
        .pm      = &pnp_bus_dev_pm_ops,
-       .dev_attrs = pnp_interface_attrs,
+       .dev_groups = pnp_dev_groups,
 };
 
 int pnp_register_driver(struct pnp_driver *drv)
index 0c201317284b40e93014d0a574b3ef0ab9d81b9b..e6c403be09a924b7fe795e973dafb227a089affe 100644 (file)
@@ -203,8 +203,8 @@ static void pnp_print_option(pnp_info_buffer_t * buffer, char *space,
        }
 }
 
-static ssize_t pnp_show_options(struct device *dmdev,
-                               struct device_attribute *attr, char *buf)
+static ssize_t options_show(struct device *dmdev, struct device_attribute *attr,
+                           char *buf)
 {
        struct pnp_dev *dev = to_pnp_dev(dmdev);
        pnp_info_buffer_t *buffer;
@@ -241,10 +241,10 @@ static ssize_t pnp_show_options(struct device *dmdev,
        kfree(buffer);
        return ret;
 }
+static DEVICE_ATTR_RO(options);
 
-static ssize_t pnp_show_current_resources(struct device *dmdev,
-                                         struct device_attribute *attr,
-                                         char *buf)
+static ssize_t resources_show(struct device *dmdev,
+                             struct device_attribute *attr, char *buf)
 {
        struct pnp_dev *dev = to_pnp_dev(dmdev);
        pnp_info_buffer_t *buffer;
@@ -331,9 +331,9 @@ static char *pnp_get_resource_value(char *buf,
        return buf;
 }
 
-static ssize_t pnp_set_current_resources(struct device *dmdev,
-                                        struct device_attribute *attr,
-                                        const char *ubuf, size_t count)
+static ssize_t resources_store(struct device *dmdev,
+                              struct device_attribute *attr, const char *ubuf,
+                              size_t count)
 {
        struct pnp_dev *dev = to_pnp_dev(dmdev);
        char *buf = (void *)ubuf;
@@ -434,9 +434,10 @@ done:
                return retval;
        return count;
 }
+static DEVICE_ATTR_RW(resources);
 
-static ssize_t pnp_show_current_ids(struct device *dmdev,
-                                   struct device_attribute *attr, char *buf)
+static ssize_t id_show(struct device *dmdev, struct device_attribute *attr,
+                      char *buf)
 {
        char *str = buf;
        struct pnp_dev *dev = to_pnp_dev(dmdev);
@@ -448,12 +449,20 @@ static ssize_t pnp_show_current_ids(struct device *dmdev,
        }
        return (str - buf);
 }
+static DEVICE_ATTR_RO(id);
 
-struct device_attribute pnp_interface_attrs[] = {
-       __ATTR(resources, S_IRUGO | S_IWUSR,
-                  pnp_show_current_resources,
-                  pnp_set_current_resources),
-       __ATTR(options, S_IRUGO, pnp_show_options, NULL),
-       __ATTR(id, S_IRUGO, pnp_show_current_ids, NULL),
-       __ATTR_NULL,
+static struct attribute *pnp_dev_attrs[] = {
+       &dev_attr_resources.attr,
+       &dev_attr_options.attr,
+       &dev_attr_id.attr,
+       NULL,
+};
+
+static const struct attribute_group pnp_dev_group = {
+       .attrs = pnp_dev_attrs,
+};
+
+const struct attribute_group *pnp_dev_groups[] = {
+       &pnp_dev_group,
+       NULL,
 };
index 3e9b6a78ad18a4dcff861886d0828b5622aad267..c9ae692d34518c97485114cae2c685e6da81e11a 100644 (file)
@@ -223,8 +223,8 @@ struct device rio_bus = {
 struct bus_type rio_bus_type = {
        .name = "rapidio",
        .match = rio_match_bus,
-       .dev_attrs = rio_dev_attrs,
-       .bus_attrs = rio_bus_attrs,
+       .dev_groups = rio_dev_groups,
+       .bus_groups = rio_bus_groups,
        .probe = rio_device_probe,
        .remove = rio_device_remove,
        .uevent = rio_uevent,
index 9331be646dc34d4075df3b6512975efe3eec8c1a..e0221c6d0cc238543cc84557a9ffb176a64c23f3 100644 (file)
@@ -27,6 +27,7 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf)                    \
                                                                        \
        return sprintf(buf, format_string, rdev->field);                \
 }                                                                      \
+static DEVICE_ATTR_RO(field);
 
 rio_config_attr(did, "0x%04x\n");
 rio_config_attr(vid, "0x%04x\n");
@@ -54,6 +55,7 @@ static ssize_t routes_show(struct device *dev, struct device_attribute *attr, ch
 
        return (str - buf);
 }
+static DEVICE_ATTR_RO(routes);
 
 static ssize_t lprev_show(struct device *dev,
                          struct device_attribute *attr, char *buf)
@@ -63,6 +65,7 @@ static ssize_t lprev_show(struct device *dev,
        return sprintf(buf, "%s\n",
                        (rdev->prev) ? rio_name(rdev->prev) : "root");
 }
+static DEVICE_ATTR_RO(lprev);
 
 static ssize_t lnext_show(struct device *dev,
                          struct device_attribute *attr, char *buf)
@@ -83,6 +86,7 @@ static ssize_t lnext_show(struct device *dev,
 
        return str - buf;
 }
+static DEVICE_ATTR_RO(lnext);
 
 static ssize_t modalias_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
@@ -92,23 +96,29 @@ static ssize_t modalias_show(struct device *dev,
        return sprintf(buf, "rapidio:v%04Xd%04Xav%04Xad%04X\n",
                       rdev->vid, rdev->did, rdev->asm_vid, rdev->asm_did);
 }
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *rio_dev_attrs[] = {
+       &dev_attr_did.attr,
+       &dev_attr_vid.attr,
+       &dev_attr_device_rev.attr,
+       &dev_attr_asm_did.attr,
+       &dev_attr_asm_vid.attr,
+       &dev_attr_asm_rev.attr,
+       &dev_attr_lprev.attr,
+       &dev_attr_destid.attr,
+       &dev_attr_modalias.attr,
+       NULL,
+};
 
-struct device_attribute rio_dev_attrs[] = {
-       __ATTR_RO(did),
-       __ATTR_RO(vid),
-       __ATTR_RO(device_rev),
-       __ATTR_RO(asm_did),
-       __ATTR_RO(asm_vid),
-       __ATTR_RO(asm_rev),
-       __ATTR_RO(lprev),
-       __ATTR_RO(destid),
-       __ATTR_RO(modalias),
-       __ATTR_NULL,
+static const struct attribute_group rio_dev_group = {
+       .attrs = rio_dev_attrs,
 };
 
-static DEVICE_ATTR(routes, S_IRUGO, routes_show, NULL);
-static DEVICE_ATTR(lnext, S_IRUGO, lnext_show, NULL);
-static DEVICE_ATTR(hopcount, S_IRUGO, hopcount_show, NULL);
+const struct attribute_group *rio_dev_groups[] = {
+       &rio_dev_group,
+       NULL,
+};
 
 static ssize_t
 rio_read_config(struct file *filp, struct kobject *kobj,
@@ -316,8 +326,18 @@ exit:
 
        return rc;
 }
+static BUS_ATTR(scan, (S_IWUSR|S_IWGRP), NULL, bus_scan_store);
+
+static struct attribute *rio_bus_attrs[] = {
+       &bus_attr_scan.attr,
+       NULL,
+};
+
+static const struct attribute_group rio_bus_group = {
+       .attrs = rio_bus_attrs,
+};
 
-struct bus_attribute rio_bus_attrs[] = {
-       __ATTR(scan, (S_IWUSR|S_IWGRP), NULL, bus_scan_store),
-       __ATTR_NULL
+const struct attribute_group *rio_bus_groups[] = {
+       &rio_bus_group,
+       NULL,
 };
index 085215cd8502f5b12ca93787a267eef1d173759a..5f99d22ad0b04534440ba1d17d247c9b127caba2 100644 (file)
@@ -48,8 +48,8 @@ extern struct rio_mport *rio_find_mport(int mport_id);
 extern int rio_mport_scan(int mport_id);
 
 /* Structures internal to the RIO core code */
-extern struct device_attribute rio_dev_attrs[];
-extern struct bus_attribute rio_bus_attrs[];
+extern const struct attribute_group *rio_dev_groups[];
+extern const struct attribute_group *rio_bus_groups[];
 
 #define RIO_GET_DID(size, x)   (size ? (x & 0xffff) : ((x & 0x00ff0000) >> 16))
 #define RIO_SET_DID(size, x)   (size ? (x & 0xffff) : ((x & 0x000000ff) << 16))
index 5adb2042e824fc30815ea891c54b89526680b4eb..cee7e2708a1fe35359eb81cc458d939e50ad1906 100644 (file)
@@ -2077,6 +2077,7 @@ dasd_eckd_build_format(struct dasd_device *base,
        int intensity = 0;
        int r0_perm;
        int nr_tracks;
+       int use_prefix;
 
        startdev = dasd_alias_get_start_dev(base);
        if (!startdev)
@@ -2106,28 +2107,46 @@ dasd_eckd_build_format(struct dasd_device *base,
                intensity = fdata->intensity;
        }
 
+       use_prefix = base_priv->features.feature[8] & 0x01;
+
        switch (intensity) {
        case 0x00:      /* Normal format */
        case 0x08:      /* Normal format, use cdl. */
                cplength = 2 + (rpt*nr_tracks);
-               datasize = sizeof(struct PFX_eckd_data) +
-                       sizeof(struct LO_eckd_data) +
-                       rpt * nr_tracks * sizeof(struct eckd_count);
+               if (use_prefix)
+                       datasize = sizeof(struct PFX_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               rpt * nr_tracks * sizeof(struct eckd_count);
+               else
+                       datasize = sizeof(struct DE_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               rpt * nr_tracks * sizeof(struct eckd_count);
                break;
        case 0x01:      /* Write record zero and format track. */
        case 0x09:      /* Write record zero and format track, use cdl. */
                cplength = 2 + rpt * nr_tracks;
-               datasize = sizeof(struct PFX_eckd_data) +
-                       sizeof(struct LO_eckd_data) +
-                       sizeof(struct eckd_count) +
-                       rpt * nr_tracks * sizeof(struct eckd_count);
+               if (use_prefix)
+                       datasize = sizeof(struct PFX_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               sizeof(struct eckd_count) +
+                               rpt * nr_tracks * sizeof(struct eckd_count);
+               else
+                       datasize = sizeof(struct DE_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               sizeof(struct eckd_count) +
+                               rpt * nr_tracks * sizeof(struct eckd_count);
                break;
        case 0x04:      /* Invalidate track. */
        case 0x0c:      /* Invalidate track, use cdl. */
                cplength = 3;
-               datasize = sizeof(struct PFX_eckd_data) +
-                       sizeof(struct LO_eckd_data) +
-                       sizeof(struct eckd_count);
+               if (use_prefix)
+                       datasize = sizeof(struct PFX_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               sizeof(struct eckd_count);
+               else
+                       datasize = sizeof(struct DE_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               sizeof(struct eckd_count);
                break;
        default:
                dev_warn(&startdev->cdev->dev,
@@ -2147,14 +2166,25 @@ dasd_eckd_build_format(struct dasd_device *base,
 
        switch (intensity & ~0x08) {
        case 0x00: /* Normal format. */
-               prefix(ccw++, (struct PFX_eckd_data *) data,
-                      fdata->start_unit, fdata->stop_unit,
-                      DASD_ECKD_CCW_WRITE_CKD, base, startdev);
-               /* grant subsystem permission to format R0 */
-               if (r0_perm)
-                       ((struct PFX_eckd_data *)data)
-                               ->define_extent.ga_extended |= 0x04;
-               data += sizeof(struct PFX_eckd_data);
+               if (use_prefix) {
+                       prefix(ccw++, (struct PFX_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_CKD, base, startdev);
+                       /* grant subsystem permission to format R0 */
+                       if (r0_perm)
+                               ((struct PFX_eckd_data *)data)
+                                       ->define_extent.ga_extended |= 0x04;
+                       data += sizeof(struct PFX_eckd_data);
+               } else {
+                       define_extent(ccw++, (struct DE_eckd_data *) data,
+                                     fdata->start_unit, fdata->stop_unit,
+                                     DASD_ECKD_CCW_WRITE_CKD, startdev);
+                       /* grant subsystem permission to format R0 */
+                       if (r0_perm)
+                               ((struct DE_eckd_data *) data)
+                                       ->ga_extended |= 0x04;
+                       data += sizeof(struct DE_eckd_data);
+               }
                ccw[-1].flags |= CCW_FLAG_CC;
                locate_record(ccw++, (struct LO_eckd_data *) data,
                              fdata->start_unit, 0, rpt*nr_tracks,
@@ -2163,11 +2193,18 @@ dasd_eckd_build_format(struct dasd_device *base,
                data += sizeof(struct LO_eckd_data);
                break;
        case 0x01: /* Write record zero + format track. */
-               prefix(ccw++, (struct PFX_eckd_data *) data,
-                      fdata->start_unit, fdata->stop_unit,
-                      DASD_ECKD_CCW_WRITE_RECORD_ZERO,
-                      base, startdev);
-               data += sizeof(struct PFX_eckd_data);
+               if (use_prefix) {
+                       prefix(ccw++, (struct PFX_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_RECORD_ZERO,
+                              base, startdev);
+                       data += sizeof(struct PFX_eckd_data);
+               } else {
+                       define_extent(ccw++, (struct DE_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_RECORD_ZERO, startdev);
+                       data += sizeof(struct DE_eckd_data);
+               }
                ccw[-1].flags |= CCW_FLAG_CC;
                locate_record(ccw++, (struct LO_eckd_data *) data,
                              fdata->start_unit, 0, rpt * nr_tracks + 1,
@@ -2176,10 +2213,17 @@ dasd_eckd_build_format(struct dasd_device *base,
                data += sizeof(struct LO_eckd_data);
                break;
        case 0x04: /* Invalidate track. */
-               prefix(ccw++, (struct PFX_eckd_data *) data,
-                      fdata->start_unit, fdata->stop_unit,
-                      DASD_ECKD_CCW_WRITE_CKD, base, startdev);
-               data += sizeof(struct PFX_eckd_data);
+               if (use_prefix) {
+                       prefix(ccw++, (struct PFX_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_CKD, base, startdev);
+                       data += sizeof(struct PFX_eckd_data);
+               } else {
+                       define_extent(ccw++, (struct DE_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_CKD, startdev);
+                       data += sizeof(struct DE_eckd_data);
+               }
                ccw[-1].flags |= CCW_FLAG_CC;
                locate_record(ccw++, (struct LO_eckd_data *) data,
                              fdata->start_unit, 0, 1,
index a3aa374799dcf99bd334b6e49c8d7753838fbdc3..1fe264379e0d10b57ddca456ce16b72c23535ccb 100644 (file)
@@ -486,7 +486,7 @@ sclp_sync_wait(void)
        timeout = 0;
        if (timer_pending(&sclp_request_timer)) {
                /* Get timeout TOD value */
-               timeout = get_tod_clock() +
+               timeout = get_tod_clock_fast() +
                          sclp_tod_from_jiffies(sclp_request_timer.expires -
                                                jiffies);
        }
@@ -508,7 +508,7 @@ sclp_sync_wait(void)
        while (sclp_running_state != sclp_running_state_idle) {
                /* Check for expired request timer */
                if (timer_pending(&sclp_request_timer) &&
-                   get_tod_clock() > timeout &&
+                   get_tod_clock_fast() > timeout &&
                    del_timer(&sclp_request_timer))
                        sclp_request_timer.function(sclp_request_timer.data);
                cpu_relax();
index 9b3a24e8d3a0e0dfec9ffcbf1757ddd37f66329e..cf31d3321dab86b889b16fa5e3b9b2e433c3992d 100644 (file)
@@ -313,7 +313,7 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
        int ret;
 
        dev_num = iminor(inode);
-       if (dev_num > MAXMINOR)
+       if (dev_num >= MAXMINOR)
                return -ENODEV;
        logptr = &sys_ser[dev_num];
 
index d7da67a31c77f606ef68445f9da446b521a4abf1..88e35d85d205f7c21de1f81860865386a7845289 100644 (file)
@@ -878,9 +878,9 @@ static void css_reset(void)
                        atomic_inc(&chpid_reset_count);
        }
        /* Wait for machine check for all channel paths. */
-       timeout = get_tod_clock() + (RCHP_TIMEOUT << 12);
+       timeout = get_tod_clock_fast() + (RCHP_TIMEOUT << 12);
        while (atomic_read(&chpid_reset_count) != 0) {
-               if (get_tod_clock() > timeout)
+               if (get_tod_clock_fast() > timeout)
                        break;
                cpu_relax();
        }
index 8ed52aa4912269405158e300f1c983577b29caec..bbd3e511c771addebda2639675d85664b3191494 100644 (file)
@@ -338,10 +338,10 @@ again:
                retries++;
 
                if (!start_time) {
-                       start_time = get_tod_clock();
+                       start_time = get_tod_clock_fast();
                        goto again;
                }
-               if ((get_tod_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE)
+               if (get_tod_clock_fast() - start_time < QDIO_BUSY_BIT_PATIENCE)
                        goto again;
        }
        if (retries) {
@@ -504,7 +504,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
        int count, stop;
        unsigned char state = 0;
 
-       q->timestamp = get_tod_clock();
+       q->timestamp = get_tod_clock_fast();
 
        /*
         * Don't check 128 buffers, as otherwise qdio_inbound_q_moved
@@ -595,7 +595,7 @@ static inline int qdio_inbound_q_done(struct qdio_q *q)
         * At this point we know, that inbound first_to_check
         * has (probably) not moved (see qdio_inbound_processing).
         */
-       if (get_tod_clock() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
+       if (get_tod_clock_fast() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%02x",
                              q->first_to_check);
                return 1;
@@ -728,7 +728,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
        int count, stop;
        unsigned char state = 0;
 
-       q->timestamp = get_tod_clock();
+       q->timestamp = get_tod_clock_fast();
 
        if (need_siga_sync(q))
                if (((queue_type(q) != QDIO_IQDIO_QFMT) &&
index feab3a5e50b5ec6f546b102b6550ff7652424cac..757eb0716d45d4f273519ca2e2dd2ed03486a4a8 100644 (file)
@@ -696,7 +696,7 @@ static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
        while ((pci_device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC,
                                        PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
                                        pci_device)) != NULL) {
-               struct blogic_adapter *adapter = adapter;
+               struct blogic_adapter *host_adapter = adapter;
                struct blogic_adapter_info adapter_info;
                enum blogic_isa_ioport mod_ioaddr_req;
                unsigned char bus;
@@ -744,9 +744,9 @@ static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
                   known and enabled, note that the particular Standard ISA I/O
                   Address should not be probed.
                 */
-               adapter->io_addr = io_addr;
-               blogic_intreset(adapter);
-               if (blogic_cmd(adapter, BLOGIC_INQ_PCI_INFO, NULL, 0,
+               host_adapter->io_addr = io_addr;
+               blogic_intreset(host_adapter);
+               if (blogic_cmd(host_adapter, BLOGIC_INQ_PCI_INFO, NULL, 0,
                                &adapter_info, sizeof(adapter_info)) ==
                                sizeof(adapter_info)) {
                        if (adapter_info.isa_port < 6)
@@ -762,7 +762,7 @@ static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
                   I/O Address assigned at system initialization.
                 */
                mod_ioaddr_req = BLOGIC_IO_DISABLE;
-               blogic_cmd(adapter, BLOGIC_MOD_IOADDR, &mod_ioaddr_req,
+               blogic_cmd(host_adapter, BLOGIC_MOD_IOADDR, &mod_ioaddr_req,
                                sizeof(mod_ioaddr_req), NULL, 0);
                /*
                   For the first MultiMaster Host Adapter enumerated,
@@ -779,12 +779,12 @@ static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
 
                        fetch_localram.offset = BLOGIC_AUTOSCSI_BASE + 45;
                        fetch_localram.count = sizeof(autoscsi_byte45);
-                       blogic_cmd(adapter, BLOGIC_FETCH_LOCALRAM,
+                       blogic_cmd(host_adapter, BLOGIC_FETCH_LOCALRAM,
                                        &fetch_localram, sizeof(fetch_localram),
                                        &autoscsi_byte45,
                                        sizeof(autoscsi_byte45));
-                       blogic_cmd(adapter, BLOGIC_GET_BOARD_ID, NULL, 0, &id,
-                                       sizeof(id));
+                       blogic_cmd(host_adapter, BLOGIC_GET_BOARD_ID, NULL, 0,
+                                       &id, sizeof(id));
                        if (id.fw_ver_digit1 == '5')
                                force_scan_order =
                                        autoscsi_byte45.force_scan_order;
index 408a42ef787a32b6d8fece98c717376ce93c1270..f0d432c139d0cecedf51295562c858d22a9f44f0 100644 (file)
@@ -771,6 +771,8 @@ static long aac_compat_do_ioctl(struct aac_dev *dev, unsigned cmd, unsigned long
 static int aac_compat_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
 {
        struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
+       if (!capable(CAP_SYS_RAWIO))
+               return -EPERM;
        return aac_compat_do_ioctl(dev, cmd, (unsigned long)arg);
 }
 
index c9382d6eee7861bf4a8bd1399cfb6180a1fb7273..1f4f22fe82812170db684cbdd406c23c4cb32f82 100644 (file)
@@ -553,16 +553,20 @@ static struct device_type fcoe_fcf_device_type = {
        .release = fcoe_fcf_device_release,
 };
 
-static struct bus_attribute fcoe_bus_attr_group[] = {
-       __ATTR(ctlr_create, S_IWUSR, NULL, fcoe_ctlr_create_store),
-       __ATTR(ctlr_destroy, S_IWUSR, NULL, fcoe_ctlr_destroy_store),
-       __ATTR_NULL
+static BUS_ATTR(ctlr_create, S_IWUSR, NULL, fcoe_ctlr_create_store);
+static BUS_ATTR(ctlr_destroy, S_IWUSR, NULL, fcoe_ctlr_destroy_store);
+
+static struct attribute *fcoe_bus_attrs[] = {
+       &bus_attr_ctlr_create.attr,
+       &bus_attr_ctlr_destroy.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(fcoe_bus);
 
 static struct bus_type fcoe_bus_type = {
        .name = "fcoe",
        .match = &fcoe_bus_match,
-       .bus_attrs = fcoe_bus_attr_group,
+       .bus_groups = fcoe_bus_groups,
 };
 
 /**
index 2ef497ebadc06500a2aa95090f26d188cf8bccb7..ee5c1833eb731cb1e0e731448c287ca1b8f5b2ad 100644 (file)
@@ -20,7 +20,7 @@
  * | Device Discovery             |       0x2095       | 0x2020-0x2022, |
  * |                              |                    | 0x2011-0x2012, |
  * |                              |                    | 0x2016         |
- * | Queue Command and IO tracing |       0x3058       | 0x3006-0x300b  |
+ * | Queue Command and IO tracing |       0x3059       | 0x3006-0x300b  |
  * |                              |                    | 0x3027-0x3028  |
  * |                              |                    | 0x303d-0x3041  |
  * |                              |                    | 0x302d,0x3033  |
index df1b30ba938cc0578d409880711d2afa5b2a8898..ff9c86b1a0d896a870dd62b22515ee5dfc629284 100644 (file)
@@ -1957,6 +1957,15 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
        que = MSW(sts->handle);
        req = ha->req_q_map[que];
 
+       /* Check for invalid queue pointer */
+       if (req == NULL ||
+           que >= find_first_zero_bit(ha->req_qid_map, ha->max_req_queues)) {
+               ql_dbg(ql_dbg_io, vha, 0x3059,
+                   "Invalid status handle (0x%x): Bad req pointer. req=%p, "
+                   "que=%u.\n", sts->handle, req, que);
+               return;
+       }
+
        /* Validate handle. */
        if (handle < req->num_outstanding_cmds)
                sp = req->outstanding_cmds[handle];
index e62d17d41d4e7b84ddf7469194aa0c63d541d59d..5693f6d7eddb8b20cd9b3e0374a4e73e60ab7aa2 100644 (file)
@@ -2854,6 +2854,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
                gd->events |= DISK_EVENT_MEDIA_CHANGE;
        }
 
+       blk_pm_runtime_init(sdp->request_queue, dev);
        add_disk(gd);
        if (sdkp->capacity)
                sd_dif_config_host(sdkp);
@@ -2862,7 +2863,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
 
        sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
                  sdp->removable ? "removable " : "");
-       blk_pm_runtime_init(sdp->request_queue, dev);
        scsi_autopm_put_device(sdp);
        put_device(&sdkp->dev);
 }
index 5cbc4bb1b395c6174e30fd0d8f73401408f526f9..df5e961484e108312f6f01765882dd4319d584f7 100644 (file)
@@ -105,8 +105,11 @@ static int scatter_elem_sz_prev = SG_SCATTER_SZ;
 static int sg_add(struct device *, struct class_interface *);
 static void sg_remove(struct device *, struct class_interface *);
 
+static DEFINE_SPINLOCK(sg_open_exclusive_lock);
+
 static DEFINE_IDR(sg_index_idr);
-static DEFINE_RWLOCK(sg_index_lock);
+static DEFINE_RWLOCK(sg_index_lock);   /* Also used to lock
+                                                          file descriptor list for device */
 
 static struct class_interface sg_interface = {
        .add_dev        = sg_add,
@@ -143,7 +146,8 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */
 } Sg_request;
 
 typedef struct sg_fd {         /* holds the state of a file descriptor */
-       struct list_head sfd_siblings; /* protected by sfd_lock of device */
+       /* sfd_siblings is protected by sg_index_lock */
+       struct list_head sfd_siblings;
        struct sg_device *parentdp;     /* owning device */
        wait_queue_head_t read_wait;    /* queue read until command done */
        rwlock_t rq_list_lock;  /* protect access to list in req_arr */
@@ -166,12 +170,13 @@ typedef struct sg_fd {            /* holds the state of a file descriptor */
 
 typedef struct sg_device { /* holds the state of each scsi generic device */
        struct scsi_device *device;
+       wait_queue_head_t o_excl_wait;  /* queue open() when O_EXCL in use */
        int sg_tablesize;       /* adapter's max scatter-gather table size */
        u32 index;              /* device index number */
-       spinlock_t sfd_lock;    /* protect file descriptor list for device */
+       /* sfds is protected by sg_index_lock */
        struct list_head sfds;
-       struct rw_semaphore o_sem;      /* exclude open should hold this rwsem */
        volatile char detached; /* 0->attached, 1->detached pending removal */
+       /* exclude protected by sg_open_exclusive_lock */
        char exclude;           /* opened for exclusive access */
        char sgdebug;           /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
        struct gendisk *disk;
@@ -220,14 +225,35 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd)
        return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE);
 }
 
+static int get_exclude(Sg_device *sdp)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&sg_open_exclusive_lock, flags);
+       ret = sdp->exclude;
+       spin_unlock_irqrestore(&sg_open_exclusive_lock, flags);
+       return ret;
+}
+
+static int set_exclude(Sg_device *sdp, char val)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&sg_open_exclusive_lock, flags);
+       sdp->exclude = val;
+       spin_unlock_irqrestore(&sg_open_exclusive_lock, flags);
+       return val;
+}
+
 static int sfds_list_empty(Sg_device *sdp)
 {
        unsigned long flags;
        int ret;
 
-       spin_lock_irqsave(&sdp->sfd_lock, flags);
+       read_lock_irqsave(&sg_index_lock, flags);
        ret = list_empty(&sdp->sfds);
-       spin_unlock_irqrestore(&sdp->sfd_lock, flags);
+       read_unlock_irqrestore(&sg_index_lock, flags);
        return ret;
 }
 
@@ -239,6 +265,7 @@ sg_open(struct inode *inode, struct file *filp)
        struct request_queue *q;
        Sg_device *sdp;
        Sg_fd *sfp;
+       int res;
        int retval;
 
        nonseekable_open(inode, filp);
@@ -267,52 +294,54 @@ sg_open(struct inode *inode, struct file *filp)
                goto error_out;
        }
 
-       if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE))) {
-               retval = -EPERM; /* Can't lock it with read only access */
-               goto error_out;
-       }
-       if (flags & O_NONBLOCK) {
-               if (flags & O_EXCL) {
-                       if (!down_write_trylock(&sdp->o_sem)) {
-                               retval = -EBUSY;
-                               goto error_out;
-                       }
-               } else {
-                       if (!down_read_trylock(&sdp->o_sem)) {
-                               retval = -EBUSY;
-                               goto error_out;
-                       }
+       if (flags & O_EXCL) {
+               if (O_RDONLY == (flags & O_ACCMODE)) {
+                       retval = -EPERM; /* Can't lock it with read only access */
+                       goto error_out;
+               }
+               if (!sfds_list_empty(sdp) && (flags & O_NONBLOCK)) {
+                       retval = -EBUSY;
+                       goto error_out;
+               }
+               res = wait_event_interruptible(sdp->o_excl_wait,
+                                          ((!sfds_list_empty(sdp) || get_exclude(sdp)) ? 0 : set_exclude(sdp, 1)));
+               if (res) {
+                       retval = res;   /* -ERESTARTSYS because signal hit process */
+                       goto error_out;
+               }
+       } else if (get_exclude(sdp)) {  /* some other fd has an exclusive lock on dev */
+               if (flags & O_NONBLOCK) {
+                       retval = -EBUSY;
+                       goto error_out;
+               }
+               res = wait_event_interruptible(sdp->o_excl_wait, !get_exclude(sdp));
+               if (res) {
+                       retval = res;   /* -ERESTARTSYS because signal hit process */
+                       goto error_out;
                }
-       } else {
-               if (flags & O_EXCL)
-                       down_write(&sdp->o_sem);
-               else
-                       down_read(&sdp->o_sem);
        }
-       /* Since write lock is held, no need to check sfd_list */
-       if (flags & O_EXCL)
-               sdp->exclude = 1;       /* used by release lock */
-
+       if (sdp->detached) {
+               retval = -ENODEV;
+               goto error_out;
+       }
        if (sfds_list_empty(sdp)) {     /* no existing opens on this device */
                sdp->sgdebug = 0;
                q = sdp->device->request_queue;
                sdp->sg_tablesize = queue_max_segments(q);
        }
-       sfp = sg_add_sfp(sdp, dev);
-       if (!IS_ERR(sfp))
+       if ((sfp = sg_add_sfp(sdp, dev)))
                filp->private_data = sfp;
-               /* retval is already provably zero at this point because of the
-                * check after retval = scsi_autopm_get_device(sdp->device))
-                */
        else {
-               retval = PTR_ERR(sfp);
-
                if (flags & O_EXCL) {
-                       sdp->exclude = 0;       /* undo if error */
-                       up_write(&sdp->o_sem);
-               } else
-                       up_read(&sdp->o_sem);
+                       set_exclude(sdp, 0);    /* undo if error */
+                       wake_up_interruptible(&sdp->o_excl_wait);
+               }
+               retval = -ENOMEM;
+               goto error_out;
+       }
+       retval = 0;
 error_out:
+       if (retval) {
                scsi_autopm_put_device(sdp->device);
 sdp_put:
                scsi_device_put(sdp->device);
@@ -329,18 +358,13 @@ sg_release(struct inode *inode, struct file *filp)
 {
        Sg_device *sdp;
        Sg_fd *sfp;
-       int excl;
 
        if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
                return -ENXIO;
        SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name));
 
-       excl = sdp->exclude;
-       sdp->exclude = 0;
-       if (excl)
-               up_write(&sdp->o_sem);
-       else
-               up_read(&sdp->o_sem);
+       set_exclude(sdp, 0);
+       wake_up_interruptible(&sdp->o_excl_wait);
 
        scsi_autopm_put_device(sdp->device);
        kref_put(&sfp->f_ref, sg_remove_sfp);
@@ -1391,9 +1415,8 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
        disk->first_minor = k;
        sdp->disk = disk;
        sdp->device = scsidp;
-       spin_lock_init(&sdp->sfd_lock);
        INIT_LIST_HEAD(&sdp->sfds);
-       init_rwsem(&sdp->o_sem);
+       init_waitqueue_head(&sdp->o_excl_wait);
        sdp->sg_tablesize = queue_max_segments(q);
        sdp->index = k;
        kref_init(&sdp->d_ref);
@@ -1526,13 +1549,11 @@ static void sg_remove(struct device *cl_dev, struct class_interface *cl_intf)
 
        /* Need a write lock to set sdp->detached. */
        write_lock_irqsave(&sg_index_lock, iflags);
-       spin_lock(&sdp->sfd_lock);
        sdp->detached = 1;
        list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) {
                wake_up_interruptible(&sfp->read_wait);
                kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP);
        }
-       spin_unlock(&sdp->sfd_lock);
        write_unlock_irqrestore(&sg_index_lock, iflags);
 
        sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic");
@@ -2043,7 +2064,7 @@ sg_add_sfp(Sg_device * sdp, int dev)
 
        sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN);
        if (!sfp)
-               return ERR_PTR(-ENOMEM);
+               return NULL;
 
        init_waitqueue_head(&sfp->read_wait);
        rwlock_init(&sfp->rq_list_lock);
@@ -2057,13 +2078,9 @@ sg_add_sfp(Sg_device * sdp, int dev)
        sfp->cmd_q = SG_DEF_COMMAND_Q;
        sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
        sfp->parentdp = sdp;
-       spin_lock_irqsave(&sdp->sfd_lock, iflags);
-       if (sdp->detached) {
-               spin_unlock_irqrestore(&sdp->sfd_lock, iflags);
-               return ERR_PTR(-ENODEV);
-       }
+       write_lock_irqsave(&sg_index_lock, iflags);
        list_add_tail(&sfp->sfd_siblings, &sdp->sfds);
-       spin_unlock_irqrestore(&sdp->sfd_lock, iflags);
+       write_unlock_irqrestore(&sg_index_lock, iflags);
        SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp));
        if (unlikely(sg_big_buff != def_reserved_size))
                sg_big_buff = def_reserved_size;
@@ -2113,9 +2130,10 @@ static void sg_remove_sfp(struct kref *kref)
        struct sg_device *sdp = sfp->parentdp;
        unsigned long iflags;
 
-       spin_lock_irqsave(&sdp->sfd_lock, iflags);
+       write_lock_irqsave(&sg_index_lock, iflags);
        list_del(&sfp->sfd_siblings);
-       spin_unlock_irqrestore(&sdp->sfd_lock, iflags);
+       write_unlock_irqrestore(&sg_index_lock, iflags);
+       wake_up_interruptible(&sdp->o_excl_wait);
 
        INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext);
        schedule_work(&sfp->ew.work);
@@ -2502,7 +2520,7 @@ static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v)
        return 0;
 }
 
-/* must be called while holding sg_index_lock and sfd_lock */
+/* must be called while holding sg_index_lock */
 static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
 {
        int k, m, new_interface, blen, usg;
@@ -2587,26 +2605,22 @@ static int sg_proc_seq_show_debug(struct seq_file *s, void *v)
 
        read_lock_irqsave(&sg_index_lock, iflags);
        sdp = it ? sg_lookup_dev(it->index) : NULL;
-       if (sdp) {
-               spin_lock(&sdp->sfd_lock);
-               if (!list_empty(&sdp->sfds)) {
-                       struct scsi_device *scsidp = sdp->device;
+       if (sdp && !list_empty(&sdp->sfds)) {
+               struct scsi_device *scsidp = sdp->device;
 
-                       seq_printf(s, " >>> device=%s ", sdp->disk->disk_name);
-                       if (sdp->detached)
-                               seq_printf(s, "detached pending close ");
-                       else
-                               seq_printf
-                                   (s, "scsi%d chan=%d id=%d lun=%d   em=%d",
-                                    scsidp->host->host_no,
-                                    scsidp->channel, scsidp->id,
-                                    scsidp->lun,
-                                    scsidp->host->hostt->emulated);
-                       seq_printf(s, " sg_tablesize=%d excl=%d\n",
-                                  sdp->sg_tablesize, sdp->exclude);
-                       sg_proc_debug_helper(s, sdp);
-               }
-               spin_unlock(&sdp->sfd_lock);
+               seq_printf(s, " >>> device=%s ", sdp->disk->disk_name);
+               if (sdp->detached)
+                       seq_printf(s, "detached pending close ");
+               else
+                       seq_printf
+                           (s, "scsi%d chan=%d id=%d lun=%d   em=%d",
+                            scsidp->host->host_no,
+                            scsidp->channel, scsidp->id,
+                            scsidp->lun,
+                            scsidp->host->hostt->emulated);
+               seq_printf(s, " sg_tablesize=%d excl=%d\n",
+                          sdp->sg_tablesize, get_exclude(sdp));
+               sg_proc_debug_helper(s, sdp);
        }
        read_unlock_irqrestore(&sg_index_lock, iflags);
        return 0;
index e55ddf7cd7c2c42274145051c16760da800ad5ee..32a811d11c25cc419fc11878a9b2d3ff7c74c4bb 100644 (file)
@@ -374,7 +374,8 @@ static ssize_t \
 attrib##_show(struct device *dev, struct device_attribute *attr, char *buf) \
 { \
        return sprintf(buf, format_string, dev_to_ssb_dev(dev)->field); \
-}
+} \
+static DEVICE_ATTR_RO(attrib);
 
 ssb_config_attr(core_num, core_index, "%u\n")
 ssb_config_attr(coreid, id.coreid, "0x%04x\n")
@@ -387,16 +388,18 @@ name_show(struct device *dev, struct device_attribute *attr, char *buf)
        return sprintf(buf, "%s\n",
                       ssb_core_name(dev_to_ssb_dev(dev)->id.coreid));
 }
-
-static struct device_attribute ssb_device_attrs[] = {
-       __ATTR_RO(name),
-       __ATTR_RO(core_num),
-       __ATTR_RO(coreid),
-       __ATTR_RO(vendor),
-       __ATTR_RO(revision),
-       __ATTR_RO(irq),
-       __ATTR_NULL,
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *ssb_device_attrs[] = {
+       &dev_attr_name.attr,
+       &dev_attr_core_num.attr,
+       &dev_attr_coreid.attr,
+       &dev_attr_vendor.attr,
+       &dev_attr_revision.attr,
+       &dev_attr_irq.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(ssb_device);
 
 static struct bus_type ssb_bustype = {
        .name           = "ssb",
@@ -407,7 +410,7 @@ static struct bus_type ssb_bustype = {
        .suspend        = ssb_device_suspend,
        .resume         = ssb_device_resume,
        .uevent         = ssb_device_uevent,
-       .dev_attrs      = ssb_device_attrs,
+       .dev_groups     = ssb_device_groups,
 };
 
 static void ssb_buses_lock(void)
index 8c811439faba85ee21ed9249a0b23af3c486fa75..87b74ca84c42154eac8c8715deea37f5b20aa5ce 100644 (file)
@@ -1957,6 +1957,7 @@ cntrlEnd:
 
                BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, OSAL_DBG, DBG_LVL_ALL, "Called IOCTL_BCM_GET_DEVICE_DRIVER_INFO\n");
 
+               memset(&DevInfo, 0, sizeof(DevInfo));
                DevInfo.MaxRDMBufferSize = BUFFER_4K;
                DevInfo.u32DSDStartOffset = EEPROM_CALPARAM_START;
                DevInfo.u32RxAlignmentCorrection = 0;
index b94a95a597d63ec6428f2b92d460fe208465d351..76d5bbd4d93c38f3e25274f8d7b337eb808f1b7d 100644 (file)
@@ -1,3 +1,4 @@
 config USB_MSI3101
        tristate "Mirics MSi3101 SDR Dongle"
        depends on USB && VIDEO_DEV && VIDEO_V4L2
+        select VIDEOBUF2_VMALLOC
index 24c7b70a6cbf401696ee3faba21047d68a75f6e2..4c3bf776bb207b755c9a5bde50aa32138a6ff09a 100644 (file)
@@ -1131,7 +1131,13 @@ static int msi3101_queue_setup(struct vb2_queue *vq,
        /* Absolute min and max number of buffers available for mmap() */
        *nbuffers = 32;
        *nplanes = 1;
-       sizes[0] = PAGE_ALIGN(3 * 3072); /* 3 * 768 * 4 */
+       /*
+        *   3, wMaxPacketSize 3x 1024 bytes
+        * 504, max IQ sample pairs per 1024 frame
+        *   2, two samples, I and Q
+        *   4, 32-bit float
+        */
+       sizes[0] = PAGE_ALIGN(3 * 504 * 2 * 4); /* = 12096 */
        dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
                        __func__, *nbuffers, sizes[0]);
        return 0;
@@ -1657,7 +1663,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
                        f->frequency * 625UL / 10UL);
 }
 
-const struct v4l2_ioctl_ops msi3101_ioctl_ops = {
+static const struct v4l2_ioctl_ops msi3101_ioctl_ops = {
        .vidioc_querycap          = msi3101_querycap,
 
        .vidioc_enum_input        = msi3101_enum_input,
index 6ccb64fb07865821741d9c3406377bf97cfe7994..6ce0af9977d8c0ccc053e98eb818a98ec8d990f7 100644 (file)
@@ -155,6 +155,9 @@ static ssize_t oz_cdev_write(struct file *filp, const char __user *buf,
        struct oz_app_hdr *app_hdr;
        struct oz_serial_ctx *ctx;
 
+       if (count > sizeof(ei->data) - sizeof(*elt) - sizeof(*app_hdr))
+               return -EINVAL;
+
        spin_lock_bh(&g_cdev.lock);
        pd = g_cdev.active_pd;
        if (pd)
index 11c047e3a901b6d95de44c8ee86bd44d5392bc58..5cd3efff97d3b3a029c328fef330aa05348eff5c 100644 (file)
@@ -1063,7 +1063,7 @@ static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg)
 
 static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt)
 {
-       struct serial_icounter_struct icount;
+       struct serial_icounter_struct icount = {};
        struct sb_uart_icount cnow;
        struct sb_uart_port *port = state->port;
 
index c97e0e154d285d8b638dc5adb7a1bd4ac764d586..7e10dcdc3090085460918be8dfa9261df14eaa13 100644 (file)
@@ -570,6 +570,7 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
        ltv_t                   *pLtv;
        bool_t                  ltvAllocated = FALSE;
        ENCSTRCT                sEncryption;
+       size_t                  len;
 
 #ifdef USE_WDS
        hcf_16                  hcfPort  = HCF_PORT_0;
@@ -686,7 +687,8 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
                                        break;
                                case CFG_CNF_OWN_NAME:
                                        memset(lp->StationName, 0, sizeof(lp->StationName));
-                                       memcpy((void *)lp->StationName, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0]);
+                                       len = min_t(size_t, pLtv->u.u16[0], sizeof(lp->StationName));
+                                       strlcpy(lp->StationName, &pLtv->u.u8[2], len);
                                        pLtv->u.u16[0] = CNV_INT_TO_LITTLE(pLtv->u.u16[0]);
                                        break;
                                case CFG_CNF_LOAD_BALANCING:
@@ -1783,6 +1785,7 @@ int wvlan_set_station_nickname(struct net_device *dev,
 {
        struct wl_private *lp = wl_priv(dev);
        unsigned long flags;
+       size_t len;
        int         ret = 0;
        /*------------------------------------------------------------------------*/
 
@@ -1793,8 +1796,8 @@ int wvlan_set_station_nickname(struct net_device *dev,
        wl_lock(lp, &flags);
 
        memset(lp->StationName, 0, sizeof(lp->StationName));
-
-       memcpy(lp->StationName, extra, wrqu->data.length);
+       len = min_t(size_t, wrqu->data.length, sizeof(lp->StationName));
+       strlcpy(lp->StationName, extra, len);
 
        /* Commit the adapter parameters */
        wl_apply(lp);
index 551c96ca60acab10e0033a8946693b1371bcbfbe..0f199f6a07385e6ea03d4c12da24863cd7e4f7cd 100644 (file)
@@ -134,10 +134,10 @@ static int pscsi_pmode_enable_hba(struct se_hba *hba, unsigned long mode_flag)
         * pSCSI Host ID and enable for phba mode
         */
        sh = scsi_host_lookup(phv->phv_host_id);
-       if (IS_ERR(sh)) {
+       if (!sh) {
                pr_err("pSCSI: Unable to locate SCSI Host for"
                        " phv_host_id: %d\n", phv->phv_host_id);
-               return PTR_ERR(sh);
+               return -EINVAL;
        }
 
        phv->phv_lld_host = sh;
@@ -515,10 +515,10 @@ static int pscsi_configure_device(struct se_device *dev)
                        sh = phv->phv_lld_host;
                } else {
                        sh = scsi_host_lookup(pdv->pdv_host_id);
-                       if (IS_ERR(sh)) {
+                       if (!sh) {
                                pr_err("pSCSI: Unable to locate"
                                        " pdv_host_id: %d\n", pdv->pdv_host_id);
-                               return PTR_ERR(sh);
+                               return -EINVAL;
                        }
                }
        } else {
index 4714c6f8da4be5fe39e797237fa8a35de0450883..d9b92b2c524d4f055a035f4343175fbdd3877c4c 100644 (file)
@@ -263,6 +263,11 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
                        sectors, cmd->se_dev->dev_attrib.max_write_same_len);
                return TCM_INVALID_CDB_FIELD;
        }
+       /* We always have ANC_SUP == 0 so setting ANCHOR is always an error */
+       if (flags[0] & 0x10) {
+               pr_warn("WRITE SAME with ANCHOR not supported\n");
+               return TCM_INVALID_CDB_FIELD;
+       }
        /*
         * Special case for WRITE_SAME w/ UNMAP=1 that ends up getting
         * translated into block discard requests within backend code.
index 3da4fd10b9f820b6409980055ae19a2b3607f713..474cd44fac14d61b530a6100d5e6060086777560 100644 (file)
@@ -82,6 +82,9 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
        mutex_lock(&g_device_mutex);
        list_for_each_entry(se_dev, &g_device_list, g_dev_node) {
 
+               if (!se_dev->dev_attrib.emulate_3pc)
+                       continue;
+
                memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
                target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]);
 
@@ -357,6 +360,7 @@ struct xcopy_pt_cmd {
        struct se_cmd se_cmd;
        struct xcopy_op *xcopy_op;
        struct completion xpt_passthrough_sem;
+       unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER];
 };
 
 static struct se_port xcopy_pt_port;
@@ -675,7 +679,8 @@ static int target_xcopy_issue_pt_cmd(struct xcopy_pt_cmd *xpt_cmd)
 
        pr_debug("target_xcopy_issue_pt_cmd(): SCSI status: 0x%02x\n",
                        se_cmd->scsi_status);
-       return 0;
+
+       return (se_cmd->scsi_status) ? -EINVAL : 0;
 }
 
 static int target_xcopy_read_source(
@@ -708,7 +713,7 @@ static int target_xcopy_read_source(
                (unsigned long long)src_lba, src_sectors, length);
 
        transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
-                               DMA_FROM_DEVICE, 0, NULL);
+                             DMA_FROM_DEVICE, 0, &xpt_cmd->sense_buffer[0]);
        xop->src_pt_cmd = xpt_cmd;
 
        rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0],
@@ -768,7 +773,7 @@ static int target_xcopy_write_destination(
                (unsigned long long)dst_lba, dst_sectors, length);
 
        transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
-                               DMA_TO_DEVICE, 0, NULL);
+                             DMA_TO_DEVICE, 0, &xpt_cmd->sense_buffer[0]);
        xop->dst_pt_cmd = xpt_cmd;
 
        rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, dst_dev, &cdb[0],
@@ -884,30 +889,42 @@ out:
 
 sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
 {
+       struct se_device *dev = se_cmd->se_dev;
        struct xcopy_op *xop = NULL;
        unsigned char *p = NULL, *seg_desc;
        unsigned int list_id, list_id_usage, sdll, inline_dl, sa;
+       sense_reason_t ret = TCM_INVALID_PARAMETER_LIST;
        int rc;
        unsigned short tdll;
 
+       if (!dev->dev_attrib.emulate_3pc) {
+               pr_err("EXTENDED_COPY operation explicitly disabled\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
        sa = se_cmd->t_task_cdb[1] & 0x1f;
        if (sa != 0x00) {
                pr_err("EXTENDED_COPY(LID4) not supported\n");
                return TCM_UNSUPPORTED_SCSI_OPCODE;
        }
 
+       xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
+       if (!xop) {
+               pr_err("Unable to allocate xcopy_op\n");
+               return TCM_OUT_OF_RESOURCES;
+       }
+       xop->xop_se_cmd = se_cmd;
+
        p = transport_kmap_data_sg(se_cmd);
        if (!p) {
                pr_err("transport_kmap_data_sg() failed in target_do_xcopy\n");
+               kfree(xop);
                return TCM_OUT_OF_RESOURCES;
        }
 
        list_id = p[0];
-       if (list_id != 0x00) {
-               pr_err("XCOPY with non zero list_id: 0x%02x\n", list_id);
-               goto out;
-       }
-       list_id_usage = (p[1] & 0x18);
+       list_id_usage = (p[1] & 0x18) >> 3;
+
        /*
         * Determine TARGET DESCRIPTOR LIST LENGTH + SEGMENT DESCRIPTOR LIST LENGTH
         */
@@ -920,13 +937,6 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
                goto out;
        }
 
-       xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
-       if (!xop) {
-               pr_err("Unable to allocate xcopy_op\n");
-               goto out;
-       }
-       xop->xop_se_cmd = se_cmd;
-
        pr_debug("Processing XCOPY with list_id: 0x%02x list_id_usage: 0x%02x"
                " tdll: %hu sdll: %u inline_dl: %u\n", list_id, list_id_usage,
                tdll, sdll, inline_dl);
@@ -935,6 +945,17 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
        if (rc <= 0)
                goto out;
 
+       if (xop->src_dev->dev_attrib.block_size !=
+           xop->dst_dev->dev_attrib.block_size) {
+               pr_err("XCOPY: Non matching src_dev block_size: %u + dst_dev"
+                      " block_size: %u currently unsupported\n",
+                       xop->src_dev->dev_attrib.block_size,
+                       xop->dst_dev->dev_attrib.block_size);
+               xcopy_pt_undepend_remotedev(xop);
+               ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+               goto out;
+       }
+
        pr_debug("XCOPY: Processed %d target descriptors, length: %u\n", rc,
                                rc * XCOPY_TARGET_DESC_LEN);
        seg_desc = &p[16];
@@ -957,7 +978,7 @@ out:
        if (p)
                transport_kunmap_data_sg(se_cmd);
        kfree(xop);
-       return TCM_INVALID_CDB_FIELD;
+       return ret;
 }
 
 static sense_reason_t target_rcr_operating_parameters(struct se_cmd *se_cmd)
index f10a6ad37c0609fd2383061d1c4f651bb9cde36b..c2301da08ac7bb0958755b5f2b1291447f4aae6a 100644 (file)
@@ -310,8 +310,6 @@ void exynos_report_trigger(struct thermal_sensor_conf *conf)
        }
 
        th_zone = conf->pzone_data;
-       if (th_zone->therm_dev)
-               return;
 
        if (th_zone->bind == false) {
                for (i = 0; i < th_zone->cool_dev_size; i++) {
index b43afda8acd163085a11eaeb851fb1869403ed4b..32f38b90c4f6a6705b44e8381ee339c2382e47bb 100644 (file)
@@ -317,6 +317,9 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
 
        con = readl(data->base + reg->tmu_ctrl);
 
+       if (pdata->test_mux)
+               con |= (pdata->test_mux << reg->test_mux_addr_shift);
+
        if (pdata->reference_voltage) {
                con &= ~(reg->buf_vref_sel_mask << reg->buf_vref_sel_shift);
                con |= pdata->reference_voltage << reg->buf_vref_sel_shift;
@@ -488,7 +491,7 @@ static const struct of_device_id exynos_tmu_match[] = {
        },
        {
                .compatible = "samsung,exynos4412-tmu",
-               .data = (void *)EXYNOS5250_TMU_DRV_DATA,
+               .data = (void *)EXYNOS4412_TMU_DRV_DATA,
        },
        {
                .compatible = "samsung,exynos5250-tmu",
@@ -629,9 +632,10 @@ static int exynos_tmu_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       if (pdata->type == SOC_ARCH_EXYNOS ||
-               pdata->type == SOC_ARCH_EXYNOS4210 ||
-                               pdata->type == SOC_ARCH_EXYNOS5440)
+       if (pdata->type == SOC_ARCH_EXYNOS4210 ||
+           pdata->type == SOC_ARCH_EXYNOS4412 ||
+           pdata->type == SOC_ARCH_EXYNOS5250 ||
+           pdata->type == SOC_ARCH_EXYNOS5440)
                data->soc = pdata->type;
        else {
                ret = -EINVAL;
index b364c9eee70103a622a852096295f4e3339b474f..3fb65547e64c9d9e3d3410f1c0ea729fb5a9332e 100644 (file)
@@ -41,7 +41,8 @@ enum calibration_mode {
 
 enum soc_type {
        SOC_ARCH_EXYNOS4210 = 1,
-       SOC_ARCH_EXYNOS,
+       SOC_ARCH_EXYNOS4412,
+       SOC_ARCH_EXYNOS5250,
        SOC_ARCH_EXYNOS5440,
 };
 
@@ -84,6 +85,7 @@ enum soc_type {
  * @triminfo_reload_shift: shift of triminfo reload enable bit in triminfo_ctrl
        reg.
  * @tmu_ctrl: TMU main controller register.
+ * @test_mux_addr_shift: shift bits of test mux address.
  * @buf_vref_sel_shift: shift bits of reference voltage in tmu_ctrl register.
  * @buf_vref_sel_mask: mask bits of reference voltage in tmu_ctrl register.
  * @therm_trip_mode_shift: shift bits of tripping mode in tmu_ctrl register.
@@ -150,6 +152,7 @@ struct exynos_tmu_registers {
        u32     triminfo_reload_shift;
 
        u32     tmu_ctrl;
+       u32     test_mux_addr_shift;
        u32     buf_vref_sel_shift;
        u32     buf_vref_sel_mask;
        u32     therm_trip_mode_shift;
@@ -257,6 +260,7 @@ struct exynos_tmu_registers {
  * @first_point_trim: temp value of the first point trimming
  * @second_point_trim: temp value of the second point trimming
  * @default_temp_offset: default temperature offset in case of no trimming
+ * @test_mux; information if SoC supports test MUX
  * @cal_type: calibration type for temperature
  * @cal_mode: calibration mode for temperature
  * @freq_clip_table: Table representing frequency reduction percentage.
@@ -286,6 +290,7 @@ struct exynos_tmu_platform_data {
        u8 first_point_trim;
        u8 second_point_trim;
        u8 default_temp_offset;
+       u8 test_mux;
 
        enum calibration_type cal_type;
        enum calibration_mode cal_mode;
index 9002499c1f69289350f2e7edd813716919927083..073c292baa53e12d0bc7c6aef2a8956a81ea4a6d 100644 (file)
@@ -90,14 +90,15 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
 };
 #endif
 
-#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412)
-static const struct exynos_tmu_registers exynos5250_tmu_registers = {
+#if defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
+static const struct exynos_tmu_registers exynos4412_tmu_registers = {
        .triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
        .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
        .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
        .triminfo_ctrl = EXYNOS_TMU_TRIMINFO_CON,
        .triminfo_reload_shift = EXYNOS_TRIMINFO_RELOAD_SHIFT,
        .tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
+       .test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT,
        .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
        .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
        .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
@@ -128,7 +129,7 @@ static const struct exynos_tmu_registers exynos5250_tmu_registers = {
        .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
 };
 
-#define EXYNOS5250_TMU_DATA \
+#define EXYNOS4412_TMU_DATA \
        .threshold_falling = 10, \
        .trigger_levels[0] = 85, \
        .trigger_levels[1] = 103, \
@@ -162,15 +163,32 @@ static const struct exynos_tmu_registers exynos5250_tmu_registers = {
                .temp_level = 103, \
        }, \
        .freq_tab_count = 2, \
-       .type = SOC_ARCH_EXYNOS, \
-       .registers = &exynos5250_tmu_registers, \
+       .registers = &exynos4412_tmu_registers, \
        .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
                        TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
                        TMU_SUPPORT_EMUL_TIME)
+#endif
 
+#if defined(CONFIG_SOC_EXYNOS4412)
+struct exynos_tmu_init_data const exynos4412_default_tmu_data = {
+       .tmu_data = {
+               {
+                       EXYNOS4412_TMU_DATA,
+                       .type = SOC_ARCH_EXYNOS4412,
+                       .test_mux = EXYNOS4412_MUX_ADDR_VALUE,
+               },
+       },
+       .tmu_count = 1,
+};
+#endif
+
+#if defined(CONFIG_SOC_EXYNOS5250)
 struct exynos_tmu_init_data const exynos5250_default_tmu_data = {
        .tmu_data = {
-               { EXYNOS5250_TMU_DATA },
+               {
+                       EXYNOS4412_TMU_DATA,
+                       .type = SOC_ARCH_EXYNOS5250,
+               },
        },
        .tmu_count = 1,
 };
index dc7feb51099b449bb680e5b9237966b8eb47295e..a1ea19d9e0a6e6bfee1a80f173dcdf4786dcc284 100644 (file)
 
 #define EXYNOS_MAX_TRIGGER_PER_REG     4
 
+/* Exynos4412 specific */
+#define EXYNOS4412_MUX_ADDR_VALUE          6
+#define EXYNOS4412_MUX_ADDR_SHIFT          20
+
 /*exynos5440 specific registers*/
 #define EXYNOS5440_TMU_S0_7_TRIM               0x000
 #define EXYNOS5440_TMU_S0_7_CTRL               0x020
@@ -138,7 +142,14 @@ extern struct exynos_tmu_init_data const exynos4210_default_tmu_data;
 #define EXYNOS4210_TMU_DRV_DATA (NULL)
 #endif
 
-#if (defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412))
+#if defined(CONFIG_SOC_EXYNOS4412)
+extern struct exynos_tmu_init_data const exynos4412_default_tmu_data;
+#define EXYNOS4412_TMU_DRV_DATA (&exynos4412_default_tmu_data)
+#else
+#define EXYNOS4412_TMU_DRV_DATA (NULL)
+#endif
+
+#if defined(CONFIG_SOC_EXYNOS5250)
 extern struct exynos_tmu_init_data const exynos5250_default_tmu_data;
 #define EXYNOS5250_TMU_DRV_DATA (&exynos5250_default_tmu_data)
 #else
index eeef0e2498caa39edc55cd3cf6345953a537f626..fdb07199d9c2693b8ae91af6d626f588ed21350b 100644 (file)
@@ -159,7 +159,7 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
 
        INIT_LIST_HEAD(&hwmon->tz_list);
        strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
-       hwmon->device = hwmon_device_register(&tz->device);
+       hwmon->device = hwmon_device_register(NULL);
        if (IS_ERR(hwmon->device)) {
                result = PTR_ERR(hwmon->device);
                goto free_mem;
index 4f8b9af54a5a75d1de884a342920ccb50f9ba869..5a47cc8c8f85ae1771c3faa1042b8f068978d570 100644 (file)
@@ -110,6 +110,7 @@ static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
                } else {
                        dev_err(bgp->dev,
                                "Failed to read PCB state. Using defaults\n");
+                       ret = 0;
                }
        }
        *temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
index f36950e4134f55deb02e3a6eb172e9481828ea18..7722cb9d5a8020076afe549192b198542461f352 100644 (file)
@@ -316,18 +316,19 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
        int phy_id = topology_physical_package_id(cpu);
        struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu);
        bool notify = false;
+       unsigned long flags;
 
        if (!phdev)
                return;
 
-       spin_lock(&pkg_work_lock);
+       spin_lock_irqsave(&pkg_work_lock, flags);
        ++pkg_work_cnt;
        if (unlikely(phy_id > max_phy_id)) {
-               spin_unlock(&pkg_work_lock);
+               spin_unlock_irqrestore(&pkg_work_lock, flags);
                return;
        }
        pkg_work_scheduled[phy_id] = 0;
-       spin_unlock(&pkg_work_lock);
+       spin_unlock_irqrestore(&pkg_work_lock, flags);
 
        enable_pkg_thres_interrupt();
        rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
@@ -397,6 +398,7 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
        int thres_count;
        u32 eax, ebx, ecx, edx;
        u8 *temp;
+       unsigned long flags;
 
        cpuid(6, &eax, &ebx, &ecx, &edx);
        thres_count = ebx & 0x07;
@@ -420,19 +422,19 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
                goto err_ret_unlock;
        }
 
-       spin_lock(&pkg_work_lock);
+       spin_lock_irqsave(&pkg_work_lock, flags);
        if (topology_physical_package_id(cpu) > max_phy_id)
                max_phy_id = topology_physical_package_id(cpu);
        temp = krealloc(pkg_work_scheduled,
                        (max_phy_id+1) * sizeof(u8), GFP_ATOMIC);
        if (!temp) {
-               spin_unlock(&pkg_work_lock);
+               spin_unlock_irqrestore(&pkg_work_lock, flags);
                err = -ENOMEM;
                goto err_ret_free;
        }
        pkg_work_scheduled = temp;
        pkg_work_scheduled[topology_physical_package_id(cpu)] = 0;
-       spin_unlock(&pkg_work_lock);
+       spin_unlock_irqrestore(&pkg_work_lock, flags);
 
        phy_dev_entry->phys_proc_id = topology_physical_package_id(cpu);
        phy_dev_entry->first_cpu = cpu;
index a93a424873faf638a004a3cabbc5f381d381027a..8096fcbe2dc171d4742b561fdcaa0f60e1894547 100644 (file)
@@ -349,7 +349,7 @@ bfin_jc_early_write(struct console *co, const char *buf, unsigned int count)
        bfin_jc_straight_buffer_write(buf, count);
 }
 
-static struct __initdata console bfin_jc_early_console = {
+static struct console bfin_jc_early_console __initdata = {
        .name   = "early_BFJC",
        .write   = bfin_jc_early_write,
        .flags   = CON_ANYTIME | CON_PRINTBUFFER,
index 44fbebab5075f98f337d880288993b01ad9e7271..3502a7bbb69eee716f21b5b5b89c14fd3f9627fb 100644 (file)
@@ -86,6 +86,21 @@ static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count)
        return i;
 }
 
+static bool hvc_dcc_check(void)
+{
+       unsigned long time = jiffies + (HZ / 10);
+
+       /* Write a test character to check if it is handled */
+       __dcc_putchar('\n');
+
+       while (time_is_after_jiffies(time)) {
+               if (!(__dcc_getstatus() & DCC_STATUS_TX))
+                       return true;
+       }
+
+       return false;
+}
+
 static const struct hv_ops hvc_dcc_get_put_ops = {
        .get_chars = hvc_dcc_get_chars,
        .put_chars = hvc_dcc_put_chars,
@@ -93,6 +108,9 @@ static const struct hv_ops hvc_dcc_get_put_ops = {
 
 static int __init hvc_dcc_console_init(void)
 {
+       if (!hvc_dcc_check())
+               return -ENODEV;
+
        hvc_instantiate(0, 0, &hvc_dcc_get_put_ops);
        return 0;
 }
@@ -100,6 +118,9 @@ console_initcall(hvc_dcc_console_init);
 
 static int __init hvc_dcc_init(void)
 {
+       if (!hvc_dcc_check())
+               return -ENODEV;
+
        hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128);
        return 0;
 }
index c791b18cdd086a1fa22b470ba8b9904dd0fc01e3..b594abfbf21e76d58acc1df534a30f603670fb5a 100644 (file)
@@ -48,6 +48,7 @@
 #include <asm/prom.h>
 #include <asm/hvsi.h>
 #include <asm/udbg.h>
+#include <asm/machdep.h>
 
 #include "hvc_console.h"
 
@@ -457,7 +458,9 @@ void __init hvc_vio_init_early(void)
        if (hvterm_priv0.proto == HV_PROTOCOL_HVSI)
                goto out;
 #endif
-       add_preferred_console("hvc", 0, NULL);
+       /* Check whether the user has requested a different console. */
+       if (!strstr(cmd_line, "console="))
+               add_preferred_console("hvc", 0, NULL);
        hvc_instantiate(0, 0, ops);
 out:
        of_node_put(stdout_node);
index 7a744b69c3d144516388d3e8a4bc2fc440fd4d34..7cdd1eb9406c11ccb4870560490f0c6036f92032 100644 (file)
@@ -767,7 +767,7 @@ static size_t __process_echoes(struct tty_struct *tty)
         * of echo overrun before the next commit), then discard enough
         * data at the tail to prevent a subsequent overrun */
        while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) {
-               if (echo_buf(ldata, tail == ECHO_OP_START)) {
+               if (echo_buf(ldata, tail) == ECHO_OP_START) {
                        if (echo_buf(ldata, tail) == ECHO_OP_ERASE_TAB)
                                tail += 3;
                        else
@@ -1752,20 +1752,14 @@ int is_ignored(int sig)
 static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 {
        struct n_tty_data *ldata = tty->disc_data;
-       int canon_change = 1;
 
-       if (old)
-               canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
-       if (canon_change) {
+       if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) {
                bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
                ldata->line_start = ldata->canon_head = ldata->read_tail;
                ldata->erasing = 0;
                ldata->lnext = 0;
        }
 
-       if (canon_change && !L_ICANON(tty) && read_cnt(ldata))
-               wake_up_interruptible(&tty->read_wait);
-
        ldata->icanon = (L_ICANON(tty) != 0);
 
        if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
@@ -1820,9 +1814,8 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
         * Fix tty hang when I_IXON(tty) is cleared, but the tty
         * been stopped by STOP_CHAR(tty) before it.
         */
-       if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow_stopped) {
+       if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow_stopped)
                start_tty(tty);
-       }
 
        /* The termios change make the tty ready for I/O */
        wake_up_interruptible(&tty->write_wait);
index d6080c3831ef98b72d8269dc3750f58f6d55bfba..cd04293695576aeec9cd25fdce8de91b97f7fd2c 100644 (file)
@@ -959,7 +959,7 @@ static int receive_flow_control(struct nozomi *dc)
                dev_err(&dc->pdev->dev,
                        "ERROR: flow control received for non-existing port\n");
                return 0;
-       };
+       }
 
        DBG1("0x%04X->0x%04X", *((u16 *)&dc->port[port].ctrl_dl),
           *((u16 *)&ctrl_dl));
@@ -1025,7 +1025,7 @@ static enum ctrl_port_type port2ctrl(enum port_type port,
                dev_err(&dc->pdev->dev,
                        "ERROR: send flow control " \
                        "received for non-existing port\n");
-       };
+       }
        return CTRL_ERROR;
 }
 
@@ -1805,7 +1805,7 @@ static int ntty_ioctl(struct tty_struct *tty,
        default:
                DBG1("ERR: 0x%08X, %d", cmd, cmd);
                break;
-       };
+       }
 
        return rval;
 }
index 570df9d2a5d2f46afc72a23d1e373f2c5aa4f3ba..e33d38cb170f97e4055a0a17eb0f4e7bc0b45d0e 100644 (file)
@@ -2322,7 +2322,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 
        if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
                fcr = uart_config[port->type].fcr;
-               if (baud < 2400 || fifo_bug) {
+               if ((baud < 2400 && !up->dma) || fifo_bug) {
                        fcr &= ~UART_FCR_TRIGGER_MASK;
                        fcr |= UART_FCR_TRIGGER_1;
                }
index daf710f5c3fc68e0197012ea129edf1a41a7ea14..4658e3e0ec4256d9b2e31f890ea72822abf47f93 100644 (file)
 
 
 struct dw8250_data {
-       int             last_lcr;
-       int             last_mcr;
-       int             line;
-       struct clk      *clk;
-       u8              usr_reg;
+       u8                      usr_reg;
+       int                     last_mcr;
+       int                     line;
+       struct clk              *clk;
+       struct uart_8250_dma    dma;
 };
 
 static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
@@ -76,17 +76,33 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
        return value;
 }
 
+static void dw8250_force_idle(struct uart_port *p)
+{
+       serial8250_clear_and_reinit_fifos(container_of
+                                         (p, struct uart_8250_port, port));
+       (void)p->serial_in(p, UART_RX);
+}
+
 static void dw8250_serial_out(struct uart_port *p, int offset, int value)
 {
        struct dw8250_data *d = p->private_data;
 
-       if (offset == UART_LCR)
-               d->last_lcr = value;
-
        if (offset == UART_MCR)
                d->last_mcr = value;
 
        writeb(value, p->membase + (offset << p->regshift));
+
+       /* Make sure LCR write wasn't ignored */
+       if (offset == UART_LCR) {
+               int tries = 1000;
+               while (tries--) {
+                       if (value == p->serial_in(p, UART_LCR))
+                               return;
+                       dw8250_force_idle(p);
+                       writeb(value, p->membase + (UART_LCR << p->regshift));
+               }
+               dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+       }
 }
 
 static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
@@ -107,13 +123,22 @@ static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
 {
        struct dw8250_data *d = p->private_data;
 
-       if (offset == UART_LCR)
-               d->last_lcr = value;
-
        if (offset == UART_MCR)
                d->last_mcr = value;
 
        writel(value, p->membase + (offset << p->regshift));
+
+       /* Make sure LCR write wasn't ignored */
+       if (offset == UART_LCR) {
+               int tries = 1000;
+               while (tries--) {
+                       if (value == p->serial_in(p, UART_LCR))
+                               return;
+                       dw8250_force_idle(p);
+                       writel(value, p->membase + (UART_LCR << p->regshift));
+               }
+               dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+       }
 }
 
 static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
@@ -131,9 +156,8 @@ static int dw8250_handle_irq(struct uart_port *p)
        if (serial8250_handle_irq(p, iir)) {
                return 1;
        } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
-               /* Clear the USR and write the LCR again. */
+               /* Clear the USR */
                (void)p->serial_in(p, d->usr_reg);
-               p->serial_out(p, UART_LCR, d->last_lcr);
 
                return 1;
        }
@@ -153,6 +177,14 @@ dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
                pm_runtime_put_sync_suspend(port->dev);
 }
 
+static bool dw8250_dma_filter(struct dma_chan *chan, void *param)
+{
+       struct dw8250_data *data = param;
+
+       return chan->chan_id == data->dma.tx_chan_id ||
+              chan->chan_id == data->dma.rx_chan_id;
+}
+
 static void dw8250_setup_port(struct uart_8250_port *up)
 {
        struct uart_port        *p = &up->port;
@@ -241,7 +273,8 @@ static int dw8250_probe_of(struct uart_port *p,
 }
 
 #ifdef CONFIG_ACPI
-static int dw8250_probe_acpi(struct uart_8250_port *up)
+static int dw8250_probe_acpi(struct uart_8250_port *up,
+                            struct dw8250_data *data)
 {
        const struct acpi_device_id *id;
        struct uart_port *p = &up->port;
@@ -260,9 +293,7 @@ static int dw8250_probe_acpi(struct uart_8250_port *up)
        if (!p->uartclk)
                p->uartclk = (unsigned int)id->driver_data;
 
-       up->dma = devm_kzalloc(p->dev, sizeof(*up->dma), GFP_KERNEL);
-       if (!up->dma)
-               return -ENOMEM;
+       up->dma = &data->dma;
 
        up->dma->rxconf.src_maxburst = p->fifosize / 4;
        up->dma->txconf.dst_maxburst = p->fifosize / 4;
@@ -270,7 +301,8 @@ static int dw8250_probe_acpi(struct uart_8250_port *up)
        return 0;
 }
 #else
-static inline int dw8250_probe_acpi(struct uart_8250_port *up)
+static inline int dw8250_probe_acpi(struct uart_8250_port *up,
+                                   struct dw8250_data *data)
 {
        return -ENODEV;
 }
@@ -314,6 +346,12 @@ static int dw8250_probe(struct platform_device *pdev)
                uart.port.uartclk = clk_get_rate(data->clk);
        }
 
+       data->dma.rx_chan_id = -1;
+       data->dma.tx_chan_id = -1;
+       data->dma.rx_param = data;
+       data->dma.tx_param = data;
+       data->dma.fn = dw8250_dma_filter;
+
        uart.port.iotype = UPIO_MEM;
        uart.port.serial_in = dw8250_serial_in;
        uart.port.serial_out = dw8250_serial_out;
@@ -324,7 +362,7 @@ static int dw8250_probe(struct platform_device *pdev)
                if (err)
                        return err;
        } else if (ACPI_HANDLE(&pdev->dev)) {
-               err = dw8250_probe_acpi(&uart);
+               err = dw8250_probe_acpi(&uart, data);
                if (err)
                        return err;
        } else {
index 5f3bba12c159eb4ceaef924495929f19eb5b4247..d1a9078003bd35caa7f6ee0f8a2f6d256751686c 100644 (file)
@@ -122,7 +122,7 @@ static int serial8250_em_probe(struct platform_device *pdev)
        up.port.dev = &pdev->dev;
        up.port.private_data = priv;
 
-       clk_enable(priv->sclk);
+       clk_prepare_enable(priv->sclk);
        up.port.uartclk = clk_get_rate(priv->sclk);
 
        up.port.iotype = UPIO_MEM32;
@@ -134,7 +134,7 @@ static int serial8250_em_probe(struct platform_device *pdev)
        ret = serial8250_register_8250_port(&up);
        if (ret < 0) {
                dev_err(&pdev->dev, "unable to register 8250 port\n");
-               clk_disable(priv->sclk);
+               clk_disable_unprepare(priv->sclk);
                return ret;
        }
 
@@ -148,7 +148,7 @@ static int serial8250_em_remove(struct platform_device *pdev)
        struct serial8250_em_priv *priv = platform_get_drvdata(pdev);
 
        serial8250_unregister_port(priv->line);
-       clk_disable(priv->sclk);
+       clk_disable_unprepare(priv->sclk);
        return 0;
 }
 
index c810da7c7a8843d452ced857d64863993365b464..4697a514b80a6d2cafcd6a2800d8cdc820c6594f 100644 (file)
@@ -9,6 +9,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License.
  */
+#undef DEBUG
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/pci.h>
@@ -27,8 +28,6 @@
 
 #include "8250.h"
 
-#undef SERIAL_DEBUG_PCI
-
 /*
  * init function returns:
  *  > 0 - number of ports
@@ -63,7 +62,7 @@ static int pci_default_setup(struct serial_private*,
 
 static void moan_device(const char *str, struct pci_dev *dev)
 {
-       printk(KERN_WARNING
+       dev_err(&dev->dev,
               "%s: %s\n"
               "Please send the output of lspci -vv, this\n"
               "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n"
@@ -233,7 +232,7 @@ static int pci_inteli960ni_init(struct pci_dev *dev)
        /* is firmware started? */
        pci_read_config_dword(dev, 0x44, (void *)&oldval);
        if (oldval == 0x00001000L) { /* RESET value */
-               printk(KERN_DEBUG "Local i960 firmware missing");
+               dev_dbg(&dev->dev, "Local i960 firmware missing\n");
                return -ENODEV;
        }
        return 0;
@@ -827,7 +826,7 @@ static int pci_netmos_9900_numports(struct pci_dev *dev)
                if (sub_serports > 0) {
                        return sub_serports;
                } else {
-                       printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n");
+                       dev_err(&dev->dev, "NetMos/Mostech serial driver ignoring port on ambiguous config.\n");
                        return 0;
                }
        }
@@ -931,7 +930,7 @@ static int pci_ite887x_init(struct pci_dev *dev)
        }
 
        if (!inta_addr[i]) {
-               printk(KERN_ERR "ite887x: could not find iobase\n");
+               dev_err(&dev->dev, "ite887x: could not find iobase\n");
                return -ENODEV;
        }
 
@@ -1024,9 +1023,9 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev)
        /* Tornado device */
        if (deviceID == 0x07000200) {
                number_uarts = ioread8(p + 4);
-               printk(KERN_DEBUG
+               dev_dbg(&dev->dev,
                        "%d ports detected on Oxford PCI Express device\n",
-                                                               number_uarts);
+                       number_uarts);
        }
        pci_iounmap(dev, p);
        return number_uarts;
@@ -1308,6 +1307,29 @@ static int pci_default_setup(struct serial_private *priv,
        return setup_port(priv, port, bar, offset, board->reg_shift);
 }
 
+static int pci_pericom_setup(struct serial_private *priv,
+                 const struct pciserial_board *board,
+                 struct uart_8250_port *port, int idx)
+{
+       unsigned int bar, offset = board->first_offset, maxnr;
+
+       bar = FL_GET_BASE(board->flags);
+       if (board->flags & FL_BASE_BARS)
+               bar += idx;
+       else
+               offset += idx * board->uart_offset;
+
+       maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >>
+               (board->reg_shift + 3);
+
+       if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
+               return 1;
+
+       port->port.uartclk = 14745600;
+
+       return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
 static int
 ce4100_serial_setup(struct serial_private *priv,
                  const struct pciserial_board *board,
@@ -1324,6 +1346,120 @@ ce4100_serial_setup(struct serial_private *priv,
        return ret;
 }
 
+#define PCI_DEVICE_ID_INTEL_BYT_UART1  0x0f0a
+#define PCI_DEVICE_ID_INTEL_BYT_UART2  0x0f0c
+
+#define BYT_PRV_CLK                    0x800
+#define BYT_PRV_CLK_EN                 (1 << 0)
+#define BYT_PRV_CLK_M_VAL_SHIFT                1
+#define BYT_PRV_CLK_N_VAL_SHIFT                16
+#define BYT_PRV_CLK_UPDATE             (1 << 31)
+
+#define BYT_GENERAL_REG                        0x808
+#define BYT_GENERAL_DIS_RTS_N_OVERRIDE (1 << 3)
+
+#define BYT_TX_OVF_INT                 0x820
+#define BYT_TX_OVF_INT_MASK            (1 << 1)
+
+static void
+byt_set_termios(struct uart_port *p, struct ktermios *termios,
+               struct ktermios *old)
+{
+       unsigned int baud = tty_termios_baud_rate(termios);
+       unsigned int m = 6912;
+       unsigned int n = 15625;
+       u32 reg;
+
+       /* For baud rates 1M, 2M, 3M and 4M the dividers must be adjusted. */
+       if (baud == 1000000 || baud == 2000000 || baud == 4000000) {
+               m = 64;
+               n = 100;
+
+               p->uartclk = 64000000;
+       } else if (baud == 3000000) {
+               m = 48;
+               n = 100;
+
+               p->uartclk = 48000000;
+       } else {
+               p->uartclk = 44236800;
+       }
+
+       /* Reset the clock */
+       reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
+       writel(reg, p->membase + BYT_PRV_CLK);
+       reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE;
+       writel(reg, p->membase + BYT_PRV_CLK);
+
+       /*
+        * If auto-handshake mechanism is not enabled,
+        * disable rts_n override
+        */
+       reg = readl(p->membase + BYT_GENERAL_REG);
+       reg &= ~BYT_GENERAL_DIS_RTS_N_OVERRIDE;
+       if (termios->c_cflag & CRTSCTS)
+               reg |= BYT_GENERAL_DIS_RTS_N_OVERRIDE;
+       writel(reg, p->membase + BYT_GENERAL_REG);
+
+       serial8250_do_set_termios(p, termios, old);
+}
+
+static bool byt_dma_filter(struct dma_chan *chan, void *param)
+{
+       return chan->chan_id == *(int *)param;
+}
+
+static int
+byt_serial_setup(struct serial_private *priv,
+                const struct pciserial_board *board,
+                struct uart_8250_port *port, int idx)
+{
+       struct uart_8250_dma *dma;
+       int ret;
+
+       dma = devm_kzalloc(port->port.dev, sizeof(*dma), GFP_KERNEL);
+       if (!dma)
+               return -ENOMEM;
+
+       switch (priv->dev->device) {
+       case PCI_DEVICE_ID_INTEL_BYT_UART1:
+               dma->rx_chan_id = 3;
+               dma->tx_chan_id = 2;
+               break;
+       case PCI_DEVICE_ID_INTEL_BYT_UART2:
+               dma->rx_chan_id = 5;
+               dma->tx_chan_id = 4;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dma->rxconf.slave_id = dma->rx_chan_id;
+       dma->rxconf.src_maxburst = 16;
+
+       dma->txconf.slave_id = dma->tx_chan_id;
+       dma->txconf.dst_maxburst = 16;
+
+       dma->fn = byt_dma_filter;
+       dma->rx_param = &dma->rx_chan_id;
+       dma->tx_param = &dma->tx_chan_id;
+
+       ret = pci_default_setup(priv, board, port, idx);
+       port->port.iotype = UPIO_MEM;
+       port->port.type = PORT_16550A;
+       port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+       port->port.set_termios = byt_set_termios;
+       port->port.fifosize = 64;
+       port->tx_loadsz = 64;
+       port->dma = dma;
+       port->capabilities = UART_CAP_FIFO | UART_CAP_AFE;
+
+       /* Disable Tx counter interrupts */
+       writel(BYT_TX_OVF_INT_MASK, port->port.membase + BYT_TX_OVF_INT);
+
+       return ret;
+}
+
 static int
 pci_omegapci_setup(struct serial_private *priv,
                      const struct pciserial_board *board,
@@ -1344,17 +1480,80 @@ pci_brcm_trumanage_setup(struct serial_private *priv,
        return ret;
 }
 
+static int pci_fintek_setup(struct serial_private *priv,
+                           const struct pciserial_board *board,
+                           struct uart_8250_port *port, int idx)
+{
+       struct pci_dev *pdev = priv->dev;
+       unsigned long base;
+       unsigned long iobase;
+       unsigned long ciobase = 0;
+       u8 config_base;
+
+       /*
+        * We are supposed to be able to read these from the PCI config space,
+        * but the values there don't seem to match what we need to use, so
+        * just use these hard-coded values for now, as they are correct.
+        */
+       switch (idx) {
+       case 0: iobase = 0xe000; config_base = 0x40; break;
+       case 1: iobase = 0xe008; config_base = 0x48; break;
+       case 2: iobase = 0xe010; config_base = 0x50; break;
+       case 3: iobase = 0xe018; config_base = 0x58; break;
+       case 4: iobase = 0xe020; config_base = 0x60; break;
+       case 5: iobase = 0xe028; config_base = 0x68; break;
+       case 6: iobase = 0xe030; config_base = 0x70; break;
+       case 7: iobase = 0xe038; config_base = 0x78; break;
+       case 8: iobase = 0xe040; config_base = 0x80; break;
+       case 9: iobase = 0xe048; config_base = 0x88; break;
+       case 10: iobase = 0xe050; config_base = 0x90; break;
+       case 11: iobase = 0xe058; config_base = 0x98; break;
+       default:
+               /* Unknown number of ports, get out of here */
+               return -EINVAL;
+       }
+
+       if (idx < 4) {
+               base = pci_resource_start(priv->dev, 3);
+               ciobase = (int)(base + (0x8 * idx));
+       }
+
+       dev_dbg(&pdev->dev, "%s: idx=%d iobase=0x%lx ciobase=0x%lx config_base=0x%2x\n",
+               __func__, idx, iobase, ciobase, config_base);
+
+       /* Enable UART I/O port */
+       pci_write_config_byte(pdev, config_base + 0x00, 0x01);
+
+       /* Select 128-byte FIFO and 8x FIFO threshold */
+       pci_write_config_byte(pdev, config_base + 0x01, 0x33);
+
+       /* LSB UART */
+       pci_write_config_byte(pdev, config_base + 0x04, (u8)(iobase & 0xff));
+
+       /* MSB UART */
+       pci_write_config_byte(pdev, config_base + 0x05, (u8)((iobase & 0xff00) >> 8));
+
+       /* irq number, this usually fails, but the spec says to do it anyway. */
+       pci_write_config_byte(pdev, config_base + 0x06, pdev->irq);
+
+       port->port.iotype = UPIO_PORT;
+       port->port.iobase = iobase;
+       port->port.mapbase = 0;
+       port->port.membase = NULL;
+       port->port.regshift = 0;
+
+       return 0;
+}
+
 static int skip_tx_en_setup(struct serial_private *priv,
                        const struct pciserial_board *board,
                        struct uart_8250_port *port, int idx)
 {
        port->port.flags |= UPF_NO_TXEN_TEST;
-       printk(KERN_DEBUG "serial8250: skipping TxEn test for device "
-                         "[%04x:%04x] subsystem [%04x:%04x]\n",
-                         priv->dev->vendor,
-                         priv->dev->device,
-                         priv->dev->subsystem_vendor,
-                         priv->dev->subsystem_device);
+       dev_dbg(&priv->dev->dev,
+               "serial8250: skipping TxEn test for device [%04x:%04x] subsystem [%04x:%04x]\n",
+               priv->dev->vendor, priv->dev->device,
+               priv->dev->subsystem_vendor, priv->dev->subsystem_device);
 
        return pci_default_setup(priv, board, port, idx);
 }
@@ -1662,6 +1861,20 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .subdevice      = PCI_ANY_ID,
                .setup          = kt_serial_setup,
        },
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BYT_UART1,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = byt_serial_setup,
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BYT_UART2,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = byt_serial_setup,
+       },
        /*
         * ITE
         */
@@ -1825,6 +2038,31 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .setup          = pci_default_setup,
                .exit           = pci_plx9050_exit,
        },
+       /*
+        * Pericom
+        */
+       {
+               .vendor         = 0x12d8,
+               .device         = 0x7952,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = pci_pericom_setup,
+       },
+       {
+               .vendor         = 0x12d8,
+               .device         = 0x7954,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = pci_pericom_setup,
+       },
+       {
+               .vendor         = 0x12d8,
+               .device         = 0x7958,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = pci_pericom_setup,
+       },
+
        /*
         * PLX
         */
@@ -2255,6 +2493,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .subdevice      = PCI_ANY_ID,
                .setup          = pci_brcm_trumanage_setup,
        },
+       {
+               .vendor         = 0x1c29,
+               .device         = 0x1104,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = pci_fintek_setup,
+       },
+       {
+               .vendor         = 0x1c29,
+               .device         = 0x1108,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = pci_fintek_setup,
+       },
+       {
+               .vendor         = 0x1c29,
+               .device         = 0x1112,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = pci_fintek_setup,
+       },
 
        /*
         * Default "match everything" terminator entry
@@ -2449,9 +2708,13 @@ enum pci_board_num_t {
        pbn_ADDIDATA_PCIe_4_3906250,
        pbn_ADDIDATA_PCIe_8_3906250,
        pbn_ce4100_1_115200,
+       pbn_byt,
        pbn_omegapci,
        pbn_NETMOS9900_2s_115200,
        pbn_brcm_trumanage,
+       pbn_fintek_4,
+       pbn_fintek_8,
+       pbn_fintek_12,
 };
 
 /*
@@ -3185,6 +3448,13 @@ static struct pciserial_board pci_boards[] = {
                .base_baud      = 921600,
                .reg_shift      = 2,
        },
+       [pbn_byt] = {
+               .flags          = FL_BASE0,
+               .num_ports      = 1,
+               .base_baud      = 2764800,
+               .uart_offset    = 0x80,
+               .reg_shift      = 2,
+       },
        [pbn_omegapci] = {
                .flags          = FL_BASE0,
                .num_ports      = 8,
@@ -3202,6 +3472,24 @@ static struct pciserial_board pci_boards[] = {
                .reg_shift      = 2,
                .base_baud      = 115200,
        },
+       [pbn_fintek_4] = {
+               .num_ports      = 4,
+               .uart_offset    = 8,
+               .base_baud      = 115200,
+               .first_offset   = 0x40,
+       },
+       [pbn_fintek_8] = {
+               .num_ports      = 8,
+               .uart_offset    = 8,
+               .base_baud      = 115200,
+               .first_offset   = 0x40,
+       },
+       [pbn_fintek_12] = {
+               .num_ports      = 12,
+               .uart_offset    = 8,
+               .base_baud      = 115200,
+               .first_offset   = 0x40,
+       },
 };
 
 static const struct pci_device_id blacklist[] = {
@@ -3362,14 +3650,15 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
                if (quirk->setup(priv, board, &uart, i))
                        break;
 
-#ifdef SERIAL_DEBUG_PCI
-               printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n",
-                      uart.port.iobase, uart.port.irq, uart.port.iotype);
-#endif
+               dev_dbg(&dev->dev, "Setup PCI port: port %lx, irq %d, type %d\n",
+                       uart.port.iobase, uart.port.irq, uart.port.iotype);
 
                priv->line[i] = serial8250_register_8250_port(&uart);
                if (priv->line[i] < 0) {
-                       printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
+                       dev_err(&dev->dev,
+                               "Couldn't register serial port %lx, irq %d, type %d, error %d\n",
+                               uart.port.iobase, uart.port.irq,
+                               uart.port.iotype, priv->line[i]);
                        break;
                }
        }
@@ -3462,7 +3751,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
        }
 
        if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
-               printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
+               dev_err(&dev->dev, "invalid driver_data: %ld\n",
                        ent->driver_data);
                return -EINVAL;
        }
@@ -3520,8 +3809,6 @@ static void pciserial_remove_one(struct pci_dev *dev)
 {
        struct serial_private *priv = pci_get_drvdata(dev);
 
-       pci_set_drvdata(dev, NULL);
-
        pciserial_remove_ports(priv);
 
        pci_disable_device(dev);
@@ -3555,7 +3842,7 @@ static int pciserial_resume_one(struct pci_dev *dev)
                err = pci_enable_device(dev);
                /* FIXME: We cannot simply error out here */
                if (err)
-                       printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n");
+                       dev_err(&dev->dev, "Unable to re-enable ports, trying to continue.\n");
                pciserial_resume_ports(priv);
        }
        return 0;
@@ -4848,6 +5135,15 @@ static struct pci_device_id serial_pci_tbl[] = {
        {       PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART,
                PCI_ANY_ID,  PCI_ANY_ID, 0, 0,
                pbn_ce4100_1_115200 },
+       /* Intel BayTrail */
+       {       PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART1,
+               PCI_ANY_ID,  PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
+               pbn_byt },
+       {       PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART2,
+               PCI_ANY_ID,  PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
+               pbn_byt },
 
        /*
         * Cronyx Omega PCI
@@ -4918,6 +5214,11 @@ static struct pci_device_id serial_pci_tbl[] = {
                0,
                0, pbn_exar_XR17V358 },
 
+       /* Fintek PCI serial cards */
+       { PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 },
+       { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 },
+       { PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 },
+
        /*
         * These entries match devices with class COMMUNICATION_SERIAL,
         * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
index febd45cd50273532e99c1a4d790d2690c6ae750d..a3817ab8602fe050e5cf2bf90a79102e73a49dbb 100644 (file)
@@ -709,7 +709,7 @@ config SERIAL_IP22_ZILOG_CONSOLE
 
 config SERIAL_SH_SCI
        tristate "SuperH SCI(F) serial port support"
-       depends on HAVE_CLK && (SUPERH || ARCH_SHMOBILE)
+       depends on HAVE_CLK && (SUPERH || ARM || COMPILE_TEST)
        select SERIAL_CORE
 
 config SERIAL_SH_SCI_NR_UARTS
@@ -1512,6 +1512,7 @@ config SERIAL_FSL_LPUART_CONSOLE
 config SERIAL_ST_ASC
        tristate "ST ASC serial port support"
        select SERIAL_CORE
+       depends on ARM || COMPILE_TEST
        help
          This driver is for the on-chip Asychronous Serial Controller on
          STMicroelectronics STi SoCs.
index 8b90f0b6dfdfe7838c43863350d93f9fccc41dae..33bd8606be6272d93f82e8aa6279cc00f6da87f0 100644 (file)
@@ -728,7 +728,6 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id)
        amba_set_drvdata(dev, uap);
        ret = uart_add_one_port(&amba_reg, &uap->port);
        if (ret) {
-               amba_set_drvdata(dev, NULL);
                amba_ports[i] = NULL;
                clk_put(uap->clk);
  unmap:
@@ -745,8 +744,6 @@ static int pl010_remove(struct amba_device *dev)
        struct uart_amba_port *uap = amba_get_drvdata(dev);
        int i;
 
-       amba_set_drvdata(dev, NULL);
-
        uart_remove_one_port(&amba_reg, &uap->port);
 
        for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
index aaa22867e656dc60a65e4249ff231b76fb907c83..7203864992a523509ee0f0f42f47c36714a886aa 100644 (file)
@@ -2147,7 +2147,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
        amba_set_drvdata(dev, uap);
        ret = uart_add_one_port(&amba_reg, &uap->port);
        if (ret) {
-               amba_set_drvdata(dev, NULL);
                amba_ports[i] = NULL;
                pl011_dma_remove(uap);
        }
@@ -2160,8 +2159,6 @@ static int pl011_remove(struct amba_device *dev)
        struct uart_amba_port *uap = amba_get_drvdata(dev);
        int i;
 
-       amba_set_drvdata(dev, NULL);
-
        uart_remove_one_port(&amba_reg, &uap->port);
 
        for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
index 569872f4c9b848fea0f81eab72dd29865b0ded41..c9f5c9dcc15c48e7a15171db06f957bfe7fc6bfd 100644 (file)
@@ -533,7 +533,7 @@ arc_uart_init_one(struct platform_device *pdev, int dev_id)
        unsigned long *plat_data;
        struct arc_uart_port *uart = &arc_uart_ports[dev_id];
 
-       plat_data = (unsigned long *)dev_get_platdata(&pdev->dev);
+       plat_data = dev_get_platdata(&pdev->dev);
        if (!plat_data)
                return -ENODEV;
 
index d067285a2d203765d38d36535d588854988f75c2..c7d99af46a966f1fdc9b9ebf0359756180e90b20 100644 (file)
@@ -99,6 +99,7 @@ static void atmel_stop_rx(struct uart_port *port);
 #define UART_PUT_RTOR(port,v)  __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
 #define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
 #define UART_GET_IP_NAME(port) __raw_readl((port)->membase + ATMEL_US_NAME)
+#define UART_GET_IP_VERSION(port) __raw_readl((port)->membase + ATMEL_US_VERSION)
 
  /* PDC registers */
 #define UART_PUT_PTCR(port,v)  __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -1499,10 +1500,11 @@ static void atmel_set_ops(struct uart_port *port)
 /*
  * Get ip name usart or uart
  */
-static int atmel_get_ip_name(struct uart_port *port)
+static void atmel_get_ip_name(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
        int name = UART_GET_IP_NAME(port);
+       u32 version;
        int usart, uart;
        /* usart and uart ascii */
        usart = 0x55534152;
@@ -1517,11 +1519,23 @@ static int atmel_get_ip_name(struct uart_port *port)
                dev_dbg(port->dev, "This is uart\n");
                atmel_port->is_usart = false;
        } else {
-               dev_err(port->dev, "Not supported ip name, set to uart\n");
-               return -EINVAL;
+               /* fallback for older SoCs: use version field */
+               version = UART_GET_IP_VERSION(port);
+               switch (version) {
+               case 0x302:
+               case 0x10213:
+                       dev_dbg(port->dev, "This version is usart\n");
+                       atmel_port->is_usart = true;
+                       break;
+               case 0x203:
+               case 0x10202:
+                       dev_dbg(port->dev, "This version is uart\n");
+                       atmel_port->is_usart = false;
+                       break;
+               default:
+                       dev_err(port->dev, "Not supported ip name nor version, set to uart\n");
+               }
        }
-
-       return 0;
 }
 
 /*
@@ -2405,9 +2419,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
        /*
         * Get port name of usart or uart
         */
-       ret = atmel_get_ip_name(&port->uart);
-       if (ret < 0)
-               goto err_add_port;
+       atmel_get_ip_name(&port->uart);
 
        return 0;
 
index 87636cc61a2130db719a4e8d92a277cf2cb52cd3..4f229703328b6e2be09e0cfb111e8e81f8be1c79 100644 (file)
@@ -766,9 +766,8 @@ static int sport_uart_probe(struct platform_device *pdev)
                        return -ENOMEM;
                }
 
-               ret = peripheral_request_list(
-                       (unsigned short *)dev_get_platdata(&pdev->dev),
-                       DRV_NAME);
+               ret = peripheral_request_list(dev_get_platdata(&pdev->dev),
+                                               DRV_NAME);
                if (ret) {
                        dev_err(&pdev->dev,
                                "Fail to request SPORT peripherals\n");
@@ -844,8 +843,7 @@ static int sport_uart_probe(struct platform_device *pdev)
 out_error_unmap:
                iounmap(sport->port.membase);
 out_error_free_peripherals:
-               peripheral_free_list(
-                       (unsigned short *)dev_get_platdata(&pdev->dev));
+               peripheral_free_list(dev_get_platdata(&pdev->dev));
 out_error_free_mem:
                kfree(sport);
                bfin_sport_uart_ports[pdev->id] = NULL;
@@ -864,8 +862,7 @@ static int sport_uart_remove(struct platform_device *pdev)
        if (sport) {
                uart_remove_one_port(&sport_uart_reg, &sport->port);
                iounmap(sport->port.membase);
-               peripheral_free_list(
-                       (unsigned short *)dev_get_platdata(&pdev->dev));
+               peripheral_free_list(dev_get_platdata(&pdev->dev));
                kfree(sport);
                bfin_sport_uart_ports[pdev->id] = NULL;
        }
index 3c75e8e0402831dc5a94dda760a2b31a9c7fd7da..869ceba2ec57b54f54a2d0a5693640e72e8125bf 100644 (file)
@@ -680,7 +680,7 @@ static int bfin_serial_startup(struct uart_port *port)
                default:
                        uart_dma_ch_rx = uart_dma_ch_tx = 0;
                        break;
-               };
+               }
 
                if (uart_dma_ch_rx &&
                        request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) {
@@ -726,7 +726,7 @@ static int bfin_serial_startup(struct uart_port *port)
 #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
        if (uart->cts_pin >= 0) {
                if (request_irq(uart->status_irq, bfin_serial_mctrl_cts_int,
-                       IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) {
+                       0, "BFIN_UART_MODEM_STATUS", uart)) {
                        uart->cts_pin = -1;
                        dev_info(port->dev, "Unable to attach BlackFin UART Modem Status interrupt.\n");
                }
@@ -765,7 +765,7 @@ static void bfin_serial_shutdown(struct uart_port *port)
                break;
        default:
                break;
-       };
+       }
 #endif
        free_irq(uart->rx_irq, uart);
        free_irq(uart->tx_irq, uart);
@@ -1240,7 +1240,7 @@ static int bfin_serial_probe(struct platform_device *pdev)
                         */
 #endif
                ret = peripheral_request_list(
-                       (unsigned short *)dev_get_platdata(&pdev->dev),
+                       dev_get_platdata(&pdev->dev),
                        DRIVER_NAME);
                if (ret) {
                        dev_err(&pdev->dev,
@@ -1358,8 +1358,7 @@ static int bfin_serial_probe(struct platform_device *pdev)
 out_error_unmap:
                iounmap(uart->port.membase);
 out_error_free_peripherals:
-               peripheral_free_list(
-                       (unsigned short *)dev_get_platdata(&pdev->dev));
+               peripheral_free_list(dev_get_platdata(&pdev->dev));
 out_error_free_mem:
                kfree(uart);
                bfin_serial_ports[pdev->id] = NULL;
@@ -1377,8 +1376,7 @@ static int bfin_serial_remove(struct platform_device *pdev)
        if (uart) {
                uart_remove_one_port(&bfin_serial_reg, &uart->port);
                iounmap(uart->port.membase);
-               peripheral_free_list(
-                       (unsigned short *)dev_get_platdata(&pdev->dev));
+               peripheral_free_list(dev_get_platdata(&pdev->dev));
                kfree(uart);
                bfin_serial_ports[pdev->id] = NULL;
        }
@@ -1432,8 +1430,8 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       ret = peripheral_request_list(
-               (unsigned short *)dev_get_platdata(&pdev->dev), DRIVER_NAME);
+       ret = peripheral_request_list(dev_get_platdata(&pdev->dev),
+                                       DRIVER_NAME);
        if (ret) {
                dev_err(&pdev->dev,
                                "fail to request bfin serial peripherals\n");
@@ -1463,8 +1461,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev)
        return 0;
 
 out_error_free_peripherals:
-       peripheral_free_list(
-               (unsigned short *)dev_get_platdata(&pdev->dev));
+       peripheral_free_list(dev_get_platdata(&pdev->dev));
 
        return ret;
 }
index 7e4e4088471cea4cbd6a67c49917b5af2540edc2..8d0b994357c856319b2ffae055d1e605a7a40706 100644 (file)
@@ -459,7 +459,6 @@ static int uart_clps711x_probe(struct platform_device *pdev)
        ret = uart_register_driver(&s->uart);
        if (ret) {
                dev_err(&pdev->dev, "Registering UART driver failed\n");
-               devm_clk_put(&pdev->dev, s->uart_clk);
                return ret;
        }
 
@@ -487,7 +486,6 @@ static int uart_clps711x_remove(struct platform_device *pdev)
        for (i = 0; i < UART_CLPS711X_NR; i++)
                uart_remove_one_port(&s->uart, &s->port[i]);
 
-       devm_clk_put(&pdev->dev, s->uart_clk);
        uart_unregister_driver(&s->uart);
 
        return 0;
index af286e6713eb914ccf30f334b69706a6510b1b97..590390970996b78fb9f274bae440625f433c7a8b 100644 (file)
@@ -1008,7 +1008,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
                return -ENODEV;
        }
 
-       pl_data = (struct ifx_modem_platform_data *)dev_get_platdata(&spi->dev);
+       pl_data = dev_get_platdata(&spi->dev);
        if (!pl_data) {
                dev_err(&spi->dev, "missing platform data!");
                return -ENODEV;
index 042aa077b5b3e166a8453ac0684da1dd024f6785..b2cfdb661947ec2690fc4840e089688151749a68 100644 (file)
@@ -223,8 +223,7 @@ struct imx_port {
        struct dma_chan         *dma_chan_rx, *dma_chan_tx;
        struct scatterlist      rx_sgl, tx_sgl[2];
        void                    *rx_buf;
-       unsigned int            rx_bytes, tx_bytes;
-       struct work_struct      tsk_dma_rx, tsk_dma_tx;
+       unsigned int            tx_bytes;
        unsigned int            dma_tx_nents;
        wait_queue_head_t       dma_wait;
 };
@@ -505,34 +504,25 @@ static void dma_tx_callback(void *data)
                dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
                return;
        }
-
-       schedule_work(&sport->tsk_dma_tx);
 }
 
-static void dma_tx_work(struct work_struct *w)
+static void imx_dma_tx(struct imx_port *sport)
 {
-       struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_tx);
        struct circ_buf *xmit = &sport->port.state->xmit;
        struct scatterlist *sgl = sport->tx_sgl;
        struct dma_async_tx_descriptor *desc;
        struct dma_chan *chan = sport->dma_chan_tx;
        struct device *dev = sport->port.dev;
        enum dma_status status;
-       unsigned long flags;
        int ret;
 
-       status = chan->device->device_tx_status(chan, (dma_cookie_t)0, NULL);
+       status = dmaengine_tx_status(chan, (dma_cookie_t)0, NULL);
        if (DMA_IN_PROGRESS == status)
                return;
 
-       spin_lock_irqsave(&sport->port.lock, flags);
        sport->tx_bytes = uart_circ_chars_pending(xmit);
-       if (sport->tx_bytes == 0) {
-               spin_unlock_irqrestore(&sport->port.lock, flags);
-               return;
-       }
 
-       if (xmit->tail > xmit->head) {
+       if (xmit->tail > xmit->head && xmit->head > 0) {
                sport->dma_tx_nents = 2;
                sg_init_table(sgl, 2);
                sg_set_buf(sgl, xmit->buf + xmit->tail,
@@ -542,7 +532,6 @@ static void dma_tx_work(struct work_struct *w)
                sport->dma_tx_nents = 1;
                sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
        }
-       spin_unlock_irqrestore(&sport->port.lock, flags);
 
        ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
        if (ret == 0) {
@@ -609,11 +598,7 @@ static void imx_start_tx(struct uart_port *port)
        }
 
        if (sport->dma_is_enabled) {
-               /*
-                * We may in the interrupt context, so arise a work_struct to
-                * do the real job.
-                */
-               schedule_work(&sport->tsk_dma_tx);
+               imx_dma_tx(sport);
                return;
        }
 
@@ -732,6 +717,7 @@ out:
        return IRQ_HANDLED;
 }
 
+static int start_rx_dma(struct imx_port *sport);
 /*
  * If the RXFIFO is filled with some data, and then we
  * arise a DMA operation to receive them.
@@ -750,7 +736,7 @@ static void imx_dma_rxint(struct imx_port *sport)
                writel(temp, sport->port.membase + UCR1);
 
                /* tell the DMA to receive the data. */
-               schedule_work(&sport->tsk_dma_rx);
+               start_rx_dma(sport);
        }
 }
 
@@ -795,8 +781,15 @@ static irqreturn_t imx_int(int irq, void *dev_id)
 static unsigned int imx_tx_empty(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
+       unsigned int ret;
+
+       ret = (readl(sport->port.membase + USR2) & USR2_TXDC) ?  TIOCSER_TEMT : 0;
 
-       return (readl(sport->port.membase + USR2) & USR2_TXDC) ?  TIOCSER_TEMT : 0;
+       /* If the TX DMA is working, return 0. */
+       if (sport->dma_is_enabled && sport->dma_is_txing)
+               ret = 0;
+
+       return ret;
 }
 
 /*
@@ -865,22 +858,6 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
 }
 
 #define RX_BUF_SIZE    (PAGE_SIZE)
-static int start_rx_dma(struct imx_port *sport);
-static void dma_rx_work(struct work_struct *w)
-{
-       struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_rx);
-       struct tty_port *port = &sport->port.state->port;
-
-       if (sport->rx_bytes) {
-               tty_insert_flip_string(port, sport->rx_buf, sport->rx_bytes);
-               tty_flip_buffer_push(port);
-               sport->rx_bytes = 0;
-       }
-
-       if (sport->dma_is_rxing)
-               start_rx_dma(sport);
-}
-
 static void imx_rx_dma_done(struct imx_port *sport)
 {
        unsigned long temp;
@@ -912,6 +889,7 @@ static void dma_rx_callback(void *data)
        struct imx_port *sport = data;
        struct dma_chan *chan = sport->dma_chan_rx;
        struct scatterlist *sgl = &sport->rx_sgl;
+       struct tty_port *port = &sport->port.state->port;
        struct dma_tx_state state;
        enum dma_status status;
        unsigned int count;
@@ -919,13 +897,15 @@ static void dma_rx_callback(void *data)
        /* unmap it first */
        dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
 
-       status = chan->device->device_tx_status(chan, (dma_cookie_t)0, &state);
+       status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
        count = RX_BUF_SIZE - state.residue;
        dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
 
        if (count) {
-               sport->rx_bytes = count;
-               schedule_work(&sport->tsk_dma_rx);
+               tty_insert_flip_string(port, sport->rx_buf, count);
+               tty_flip_buffer_push(port);
+
+               start_rx_dma(sport);
        } else
                imx_rx_dma_done(sport);
 }
@@ -1007,7 +987,6 @@ static int imx_uart_dma_init(struct imx_port *sport)
                ret = -ENOMEM;
                goto err;
        }
-       sport->rx_bytes = 0;
 
        /* Prepare for TX : */
        sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");
@@ -1038,11 +1017,7 @@ err:
 static void imx_enable_dma(struct imx_port *sport)
 {
        unsigned long temp;
-       struct tty_port *port = &sport->port.state->port;
 
-       port->low_latency = 1;
-       INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
-       INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
        init_waitqueue_head(&sport->dma_wait);
 
        /* set UCR1 */
@@ -1063,7 +1038,6 @@ static void imx_enable_dma(struct imx_port *sport)
 static void imx_disable_dma(struct imx_port *sport)
 {
        unsigned long temp;
-       struct tty_port *port = &sport->port.state->port;
 
        /* clear UCR1 */
        temp = readl(sport->port.membase + UCR1);
@@ -1081,7 +1055,6 @@ static void imx_disable_dma(struct imx_port *sport)
        writel(temp, sport->port.membase + UCR4);
 
        sport->dma_is_enabled = 0;
-       port->low_latency = 0;
 }
 
 /* half the RX buffer size */
@@ -1303,6 +1276,16 @@ static void imx_shutdown(struct uart_port *port)
        clk_disable_unprepare(sport->clk_ipg);
 }
 
+static void imx_flush_buffer(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       if (sport->dma_is_enabled) {
+               sport->tx_bytes = 0;
+               dmaengine_terminate_all(sport->dma_chan_tx);
+       }
+}
+
 static void
 imx_set_termios(struct uart_port *port, struct ktermios *termios,
                   struct ktermios *old)
@@ -1539,7 +1522,7 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser)
                ret = -EINVAL;
        if (sport->port.uartclk / 16 != ser->baud_base)
                ret = -EINVAL;
-       if ((void *)sport->port.mapbase != ser->iomem_base)
+       if (sport->port.mapbase != (unsigned long)ser->iomem_base)
                ret = -EINVAL;
        if (sport->port.iobase != ser->port)
                ret = -EINVAL;
@@ -1623,6 +1606,7 @@ static struct uart_ops imx_pops = {
        .break_ctl      = imx_break_ctl,
        .startup        = imx_startup,
        .shutdown       = imx_shutdown,
+       .flush_buffer   = imx_flush_buffer,
        .set_termios    = imx_set_termios,
        .type           = imx_type,
        .release_port   = imx_release_port,
index cb3c81eb09964dd6c20a998df74a787717192dcd..1d9420548e169a312e2f6ecacb44948e560c0869 100644 (file)
@@ -832,7 +832,7 @@ ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag,
                up->curregs[5] |= Tx8;
                up->parity_mask = 0xff;
                break;
-       };
+       }
        up->curregs[4] &= ~0x0c;
        if (cflag & CSTOPB)
                up->curregs[4] |= SB2;
index b2e707aa603a52daeff7762bf81a7e82397fc831..8d71e4047bb300d0e1a12af83f2263297c4a29bc 100644 (file)
@@ -690,7 +690,7 @@ static void max310x_handle_tx(struct uart_port *port)
                        max310x_port_write(port, MAX310X_THR_REG,
                                           xmit->buf[xmit->tail]);
                        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-               };
+               }
        }
 
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
index d3db042f649eda154bdbe1e7feb81dc5822f8653..52c930fac210c3888cb253387fe7a6ab49327878 100644 (file)
@@ -293,7 +293,7 @@ static void serial_hsu_enable_ms(struct uart_port *port)
        serial_out(up, UART_IER, up->ier);
 }
 
-void hsu_dma_tx(struct uart_hsu_port *up)
+static void hsu_dma_tx(struct uart_hsu_port *up)
 {
        struct circ_buf *xmit = &up->port.state->xmit;
        struct hsu_dma_buffer *dbuf = &up->txbuf;
@@ -340,7 +340,8 @@ void hsu_dma_tx(struct uart_hsu_port *up)
 }
 
 /* The buffer is already cache coherent */
-void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf)
+static void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc,
+                                       struct hsu_dma_buffer *dbuf)
 {
        dbuf->ofs = 0;
 
@@ -386,7 +387,8 @@ static void serial_hsu_stop_tx(struct uart_port *port)
 
 /* This is always called in spinlock protected mode, so
  * modify timeout timer is safe here */
-void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts, unsigned long *flags)
+static void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts,
+                       unsigned long *flags)
 {
        struct hsu_dma_buffer *dbuf = &up->rxbuf;
        struct hsu_dma_chan *chan = up->rxc;
@@ -1183,7 +1185,7 @@ static struct console serial_hsu_console = {
 #define SERIAL_HSU_CONSOLE     NULL
 #endif
 
-struct uart_ops serial_hsu_pops = {
+static struct uart_ops serial_hsu_pops = {
        .tx_empty       = serial_hsu_tx_empty,
        .set_mctrl      = serial_hsu_set_mctrl,
        .get_mctrl      = serial_hsu_get_mctrl,
@@ -1451,7 +1453,6 @@ static void serial_hsu_remove(struct pci_dev *pdev)
                uart_remove_one_port(&serial_hsu_reg, &up->port);
        }
 
-       pci_set_drvdata(pdev, NULL);
        free_irq(pdev->irq, priv);
        pci_disable_device(pdev);
 }
@@ -1504,4 +1505,4 @@ module_init(hsu_pci_init);
 module_exit(hsu_pci_exit);
 
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:medfield-hsu");
+MODULE_DEVICE_TABLE(pci, pci_ids);
index 5be1df39f9f5f6b8fe0671bba23ba8f566e685f4..ec06505e3ae63704d73f44ad23252b3de7bc6656 100644 (file)
@@ -1301,7 +1301,6 @@ static struct uart_ops mpc52xx_uart_ops = {
        .shutdown       = mpc52xx_uart_shutdown,
        .set_termios    = mpc52xx_uart_set_termios,
 /*     .pm             = mpc52xx_uart_pm,              Not supported yet */
-/*     .set_wake       = mpc52xx_uart_set_wake,        Not supported yet */
        .type           = mpc52xx_uart_type,
        .release_port   = mpc52xx_uart_release_port,
        .request_port   = mpc52xx_uart_request_port,
@@ -1766,7 +1765,7 @@ mpc52xx_uart_of_remove(struct platform_device *op)
 static int
 mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
 {
-       struct uart_port *port = (struct uart_port *) platform_get_drvdata(op);
+       struct uart_port *port = platform_get_drvdata(op);
 
        if (port)
                uart_suspend_port(&mpc52xx_uart_driver, port);
@@ -1777,7 +1776,7 @@ mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
 static int
 mpc52xx_uart_of_resume(struct platform_device *op)
 {
-       struct uart_port *port = (struct uart_port *) platform_get_drvdata(op);
+       struct uart_port *port = platform_get_drvdata(op);
 
        if (port)
                uart_resume_port(&mpc52xx_uart_driver, port);
index 8d702677acc5911c002134f85236090eeb793d10..e30a3ca3cea39f8b34765401e1fad569265f7ebb 100644 (file)
@@ -2030,7 +2030,7 @@ static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi,
 {
        struct mpsc_pdata       *pdata;
 
-       pdata = (struct mpsc_pdata *)dev_get_platdata(&pd->dev);
+       pdata = dev_get_platdata(&pd->dev);
 
        pi->port.uartclk = pdata->brg_clk_freq;
        pi->port.iotype = UPIO_MEM;
index a67e7081f0018d640ecef090394be1f828ec04c5..db0448ae59dc85557d7a4b2c16903a602c37f2e5 100644 (file)
@@ -43,6 +43,7 @@
 
 #include <linux/kthread.h>
 #include <linux/spi/spi.h>
+#include <linux/pm.h>
 
 #include "mrst_max3110.h"
 
@@ -61,6 +62,7 @@ struct uart_max3110 {
        struct task_struct *main_thread;
        struct task_struct *read_thread;
        struct mutex thread_mutex;
+       struct mutex io_mutex;
 
        u32 baud;
        u16 cur_conf;
@@ -90,6 +92,7 @@ static int max3110_write_then_read(struct uart_max3110 *max,
        struct spi_transfer     x;
        int ret;
 
+       mutex_lock(&max->io_mutex);
        spi_message_init(&message);
        memset(&x, 0, sizeof x);
        x.len = len;
@@ -104,6 +107,7 @@ static int max3110_write_then_read(struct uart_max3110 *max,
 
        /* Do the i/o */
        ret = spi_sync(spi, &message);
+       mutex_unlock(&max->io_mutex);
        return ret;
 }
 
@@ -491,19 +495,9 @@ static int serial_m3110_startup(struct uart_port *port)
        port->state->port.low_latency = 1;
 
        if (max->irq) {
-               max->read_thread = NULL;
-               ret = request_irq(max->irq, serial_m3110_irq,
-                               IRQ_TYPE_EDGE_FALLING, "max3110", max);
-               if (ret) {
-                       max->irq = 0;
-                       pr_err(PR_FMT "unable to allocate IRQ, polling\n");
-               }  else {
-                       /* Enable RX IRQ only */
-                       config |= WC_RXA_IRQ_ENABLE;
-               }
-       }
-
-       if (max->irq == 0) {
+               /* Enable RX IRQ only */
+               config |= WC_RXA_IRQ_ENABLE;
+       } else {
                /* If IRQ is disabled, start a read thread for input data */
                max->read_thread =
                        kthread_run(max3110_read_thread, max, "max3110_read");
@@ -517,8 +511,6 @@ static int serial_m3110_startup(struct uart_port *port)
 
        ret = max3110_out(max, config);
        if (ret) {
-               if (max->irq)
-                       free_irq(max->irq, max);
                if (max->read_thread)
                        kthread_stop(max->read_thread);
                max->read_thread = NULL;
@@ -540,9 +532,6 @@ static void serial_m3110_shutdown(struct uart_port *port)
                max->read_thread = NULL;
        }
 
-       if (max->irq)
-               free_irq(max->irq, max);
-
        /* Disable interrupts from this port */
        config = WC_TAG | WC_SW_SHDI;
        max3110_out(max, config);
@@ -749,7 +738,8 @@ static int serial_m3110_suspend(struct device *dev)
        struct spi_device *spi = to_spi_device(dev);
        struct uart_max3110 *max = spi_get_drvdata(spi);
 
-       disable_irq(max->irq);
+       if (max->irq > 0)
+               disable_irq(max->irq);
        uart_suspend_port(&serial_m3110_reg, &max->port);
        max3110_out(max, max->cur_conf | WC_SW_SHDI);
        return 0;
@@ -762,7 +752,8 @@ static int serial_m3110_resume(struct device *dev)
 
        max3110_out(max, max->cur_conf);
        uart_resume_port(&serial_m3110_reg, &max->port);
-       enable_irq(max->irq);
+       if (max->irq > 0)
+               enable_irq(max->irq);
        return 0;
 }
 
@@ -803,6 +794,7 @@ static int serial_m3110_probe(struct spi_device *spi)
        max->irq = (u16)spi->irq;
 
        mutex_init(&max->thread_mutex);
+       mutex_init(&max->io_mutex);
 
        max->word_7bits = 0;
        max->parity = 0;
@@ -840,6 +832,16 @@ static int serial_m3110_probe(struct spi_device *spi)
                goto err_kthread;
        }
 
+       if (max->irq) {
+               ret = request_irq(max->irq, serial_m3110_irq,
+                               IRQ_TYPE_EDGE_FALLING, "max3110", max);
+               if (ret) {
+                       max->irq = 0;
+                       dev_warn(&spi->dev,
+                       "unable to allocate IRQ, will use polling method\n");
+               }
+       }
+
        spi_set_drvdata(spi, max);
        pmax = max;
 
@@ -867,6 +869,9 @@ static int serial_m3110_remove(struct spi_device *dev)
 
        free_page((unsigned long)max->con_xmit.buf);
 
+       if (max->irq)
+               free_irq(max->irq, max);
+
        if (max->main_thread)
                kthread_stop(max->main_thread);
 
index 10e9d70b5c4066fced345848f592dc11582c1cfb..d8b6fee77a039220ae8d7d8f08a9ff8e08c7066f 100644 (file)
@@ -39,6 +39,7 @@
 #include <asm/cacheflush.h>
 
 #define MXS_AUART_PORTS 5
+#define MXS_AUART_FIFO_SIZE            16
 
 #define AUART_CTRL0                    0x00000000
 #define AUART_CTRL0_SET                        0x00000004
@@ -548,6 +549,9 @@ static int mxs_auart_dma_init(struct mxs_auart_port *s)
        s->flags |= MXS_AUART_DMA_ENABLED;
        dev_dbg(s->dev, "enabled the DMA support.");
 
+       /* The DMA buffer is now the FIFO the TTY subsystem can use */
+       s->port.fifosize = UART_XMIT_SIZE;
+
        return 0;
 
 err_out:
@@ -741,6 +745,9 @@ static int mxs_auart_startup(struct uart_port *u)
        writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN,
                        u->membase + AUART_INTR);
 
+       /* Reset FIFO size (it could have changed if DMA was enabled) */
+       u->fifosize = MXS_AUART_FIFO_SIZE;
+
        /*
         * Enable fifo so all four bytes of a DMA word are written to
         * output (otherwise, only the LSB is written, ie. 1 in 4 bytes)
@@ -1056,7 +1063,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
        s->port.membase = ioremap(r->start, resource_size(r));
        s->port.ops = &mxs_auart_ops;
        s->port.iotype = UPIO_MEM;
-       s->port.fifosize = 16;
+       s->port.fifosize = MXS_AUART_FIFO_SIZE;
        s->port.uartclk = clk_get_rate(s->clk);
        s->port.type = PORT_IMX;
        s->port.dev = s->dev = &pdev->dev;
index 816d1a23f9d0ef725318afbf01448aa587a3371c..fa511ebab67c67efda853be187ca670e20db4679 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/irq.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_data/serial-omap.h>
@@ -134,6 +135,7 @@ struct uart_omap_port {
        struct uart_port        port;
        struct uart_omap_dma    uart_dma;
        struct device           *dev;
+       int                     wakeirq;
 
        unsigned char           ier;
        unsigned char           lcr;
@@ -175,7 +177,7 @@ struct uart_omap_port {
        bool                    is_suspending;
 };
 
-#define to_uart_omap_port(p)   ((container_of((p), struct uart_omap_port, port)))
+#define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port)))
 
 static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
 
@@ -214,10 +216,23 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
        return pdata->get_context_loss_count(up->dev);
 }
 
+static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up,
+                                      bool enable)
+{
+       if (!up->wakeirq)
+               return;
+
+       if (enable)
+               enable_irq(up->wakeirq);
+       else
+               disable_irq(up->wakeirq);
+}
+
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 {
        struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
+       serial_omap_enable_wakeirq(up, enable);
        if (!pdata || !pdata->enable_wakeup)
                return;
 
@@ -242,12 +257,12 @@ serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
        unsigned int n16 = port->uartclk / (16 * baud);
        int baudAbsDiff13 = baud - (port->uartclk / (13 * n13));
        int baudAbsDiff16 = baud - (port->uartclk / (16 * n16));
-       if(baudAbsDiff13 < 0)
+       if (baudAbsDiff13 < 0)
                baudAbsDiff13 = -baudAbsDiff13;
-       if(baudAbsDiff16 < 0)
+       if (baudAbsDiff16 < 0)
                baudAbsDiff16 = -baudAbsDiff16;
 
-       return (baudAbsDiff13 > baudAbsDiff16);
+       return (baudAbsDiff13 >= baudAbsDiff16);
 }
 
 /*
@@ -258,13 +273,13 @@ serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
 static unsigned int
 serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
 {
-       unsigned int divisor;
+       unsigned int mode;
 
        if (!serial_omap_baud_is_mode16(port, baud))
-               divisor = 13;
+               mode = 13;
        else
-               divisor = 16;
-       return port->uartclk/(baud * divisor);
+               mode = 16;
+       return port->uartclk/(mode * baud);
 }
 
 static void serial_omap_enable_ms(struct uart_port *port)
@@ -283,28 +298,40 @@ static void serial_omap_enable_ms(struct uart_port *port)
 static void serial_omap_stop_tx(struct uart_port *port)
 {
        struct uart_omap_port *up = to_uart_omap_port(port);
-       struct circ_buf *xmit = &up->port.state->xmit;
        int res;
 
        pm_runtime_get_sync(up->dev);
 
-       /* handle rs485 */
+       /* Handle RS-485 */
        if (up->rs485.flags & SER_RS485_ENABLED) {
-               /* do nothing if current tx not yet completed */
-               res = serial_in(up, UART_LSR) & UART_LSR_TEMT;
-               if (!res)
-                       return;
-
-               /* if there's no more data to send, turn off rts */
-               if (uart_circ_empty(xmit)) {
-                       /* if rts not already disabled */
+               if (up->scr & OMAP_UART_SCR_TX_EMPTY) {
+                       /* THR interrupt is fired when both TX FIFO and TX
+                        * shift register are empty. This means there's nothing
+                        * left to transmit now, so make sure the THR interrupt
+                        * is fired when TX FIFO is below the trigger level,
+                        * disable THR interrupts and toggle the RS-485 GPIO
+                        * data direction pin if needed.
+                        */
+                       up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+                       serial_out(up, UART_OMAP_SCR, up->scr);
                        res = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
                        if (gpio_get_value(up->rts_gpio) != res) {
-                               if (up->rs485.delay_rts_after_send > 0) {
+                               if (up->rs485.delay_rts_after_send > 0)
                                        mdelay(up->rs485.delay_rts_after_send);
-                               }
                                gpio_set_value(up->rts_gpio, res);
                        }
+               } else {
+                       /* We're asked to stop, but there's still stuff in the
+                        * UART FIFO, so make sure the THR interrupt is fired
+                        * when both TX FIFO and TX shift register are empty.
+                        * The next THR interrupt (if no transmission is started
+                        * in the meantime) will indicate the end of a
+                        * transmission. Therefore we _don't_ disable THR
+                        * interrupts in this situation.
+                        */
+                       up->scr |= OMAP_UART_SCR_TX_EMPTY;
+                       serial_out(up, UART_OMAP_SCR, up->scr);
+                       return;
                }
        }
 
@@ -384,15 +411,18 @@ static void serial_omap_start_tx(struct uart_port *port)
 
        pm_runtime_get_sync(up->dev);
 
-       /* handle rs485 */
+       /* Handle RS-485 */
        if (up->rs485.flags & SER_RS485_ENABLED) {
+               /* Fire THR interrupts when FIFO is below trigger level */
+               up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+               serial_out(up, UART_OMAP_SCR, up->scr);
+
                /* if rts not already enabled */
                res = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0;
                if (gpio_get_value(up->rts_gpio) != res) {
                        gpio_set_value(up->rts_gpio, res);
-                       if (up->rs485.delay_rts_before_send > 0) {
+                       if (up->rs485.delay_rts_before_send > 0)
                                mdelay(up->rs485.delay_rts_before_send);
-                       }
                }
        }
 
@@ -699,6 +729,20 @@ static int serial_omap_startup(struct uart_port *port)
        if (retval)
                return retval;
 
+       /* Optional wake-up IRQ */
+       if (up->wakeirq) {
+               retval = request_irq(up->wakeirq, serial_omap_irq,
+                                    up->port.irqflags, up->name, up);
+               if (retval) {
+                       free_irq(up->port.irq, up);
+                       return retval;
+               }
+               disable_irq(up->wakeirq);
+       } else {
+               dev_info(up->port.dev, "no wakeirq for uart%d\n",
+                        up->port.line);
+       }
+
        dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line);
 
        pm_runtime_get_sync(up->dev);
@@ -787,6 +831,8 @@ static void serial_omap_shutdown(struct uart_port *port)
        pm_runtime_mark_last_busy(up->dev);
        pm_runtime_put_autosuspend(up->dev);
        free_irq(up->port.irq, up);
+       if (up->wakeirq)
+               free_irq(up->wakeirq, up);
 }
 
 static void serial_omap_uart_qos_work(struct work_struct *work)
@@ -938,7 +984,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
         */
 
        /* Set receive FIFO threshold to 16 characters and
-        * transmit FIFO threshold to 16 spaces
+        * transmit FIFO threshold to 32 spaces
         */
        up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
        up->fcr &= ~OMAP_UART_FCR_TX_FIFO_TRIG_MASK;
@@ -1060,15 +1106,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
        dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
 }
 
-static int serial_omap_set_wake(struct uart_port *port, unsigned int state)
-{
-       struct uart_omap_port *up = to_uart_omap_port(port);
-
-       serial_omap_enable_wakeup(up, state);
-
-       return 0;
-}
-
 static void
 serial_omap_pm(struct uart_port *port, unsigned int state,
               unsigned int oldstate)
@@ -1353,6 +1390,15 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
        up->ier = mode;
        serial_out(up, UART_IER, up->ier);
 
+       /* If RS-485 is disabled, make sure the THR interrupt is fired when
+        * TX FIFO is below the trigger level.
+        */
+       if (!(up->rs485.flags & SER_RS485_ENABLED) &&
+           (up->scr & OMAP_UART_SCR_TX_EMPTY)) {
+               up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+               serial_out(up, UART_OMAP_SCR, up->scr);
+       }
+
        spin_unlock_irqrestore(&up->port.lock, flags);
        pm_runtime_mark_last_busy(up->dev);
        pm_runtime_put_autosuspend(up->dev);
@@ -1401,7 +1447,6 @@ static struct uart_ops serial_omap_pops = {
        .shutdown       = serial_omap_shutdown,
        .set_termios    = serial_omap_set_termios,
        .pm             = serial_omap_pm,
-       .set_wake       = serial_omap_set_wake,
        .type           = serial_omap_type,
        .release_port   = serial_omap_release_port,
        .request_port   = serial_omap_request_port,
@@ -1582,11 +1627,23 @@ static int serial_omap_probe(struct platform_device *pdev)
        struct uart_omap_port   *up;
        struct resource         *mem, *irq;
        struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev);
-       int ret;
+       int ret, uartirq = 0, wakeirq = 0;
 
+       /* The optional wakeirq may be specified in the board dts file */
        if (pdev->dev.of_node) {
+               uartirq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+               if (!uartirq)
+                       return -EPROBE_DEFER;
+               wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1);
                omap_up_info = of_get_uart_port_info(&pdev->dev);
                pdev->dev.platform_data = omap_up_info;
+       } else {
+               irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+               if (!irq) {
+                       dev_err(&pdev->dev, "no irq resource?\n");
+                       return -ENODEV;
+               }
+               uartirq = irq->start;
        }
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1595,12 +1652,6 @@ static int serial_omap_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!irq) {
-               dev_err(&pdev->dev, "no irq resource?\n");
-               return -ENODEV;
-       }
-
        if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
                                pdev->dev.driver->name)) {
                dev_err(&pdev->dev, "memory region already claimed\n");
@@ -1634,7 +1685,8 @@ static int serial_omap_probe(struct platform_device *pdev)
        up->port.dev = &pdev->dev;
        up->port.type = PORT_OMAP;
        up->port.iotype = UPIO_MEM;
-       up->port.irq = irq->start;
+       up->port.irq = uartirq;
+       up->wakeirq = wakeirq;
 
        up->port.regshift = 2;
        up->port.fifosize = 64;
@@ -1670,8 +1722,9 @@ static int serial_omap_probe(struct platform_device *pdev)
        up->port.uartclk = omap_up_info->uartclk;
        if (!up->port.uartclk) {
                up->port.uartclk = DEFAULT_CLK_SPEED;
-               dev_warn(&pdev->dev, "No clock speed specified: using default:"
-                                               "%d\n", DEFAULT_CLK_SPEED);
+               dev_warn(&pdev->dev,
+                        "No clock speed specified: using default: %d\n",
+                        DEFAULT_CLK_SPEED);
        }
 
        up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
index 44077c0b7670075625650c9d00cc98a63bb90f5c..0aa2b528ef3d76363c3cd9dcc4ebd96adee850a8 100644 (file)
@@ -1614,7 +1614,6 @@ static struct uart_ops pch_uart_ops = {
        .shutdown = pch_uart_shutdown,
        .set_termios = pch_uart_set_termios,
 /*     .pm             = pch_uart_pm,          Not supported yet */
-/*     .set_wake       = pch_uart_set_wake,    Not supported yet */
        .type = pch_uart_type,
        .release_port = pch_uart_release_port,
        .request_port = pch_uart_request_port,
@@ -1996,6 +1995,8 @@ module_exit(pch_uart_module_exit);
 
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("Intel EG20T PCH UART PCI Driver");
+MODULE_DEVICE_TABLE(pci, pch_uart_pci_id);
+
 module_param(default_baud, uint, S_IRUGO);
 MODULE_PARM_DESC(default_baud,
                  "Default BAUD for initial driver state and console (default 9600)");
index f87f1a0c8c6ee577860e37e7a95038e61776e1cb..95917cefe14f53e3a03bf099844d8351647cce31 100644 (file)
@@ -1072,7 +1072,7 @@ static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag,
                uap->curregs[5] |= Tx8;
                uap->parity_mask = 0xff;
                break;
-       };
+       }
        uap->curregs[4] &= ~(SB_MASK);
        if (cflag & CSTOPB)
                uap->curregs[4] |= SB2;
index ba25722a7131ba11246c88a26a800fa7493b69bb..753d4525b36743ab23aaeed37c0205b39fb21788 100644 (file)
@@ -647,7 +647,10 @@ void sa1100_register_uart_fns(struct sa1100_port_fns *fns)
                sa1100_pops.set_mctrl = fns->set_mctrl;
 
        sa1100_pops.pm       = fns->pm;
-       sa1100_pops.set_wake = fns->set_wake;
+       /*
+        * FIXME: fns->set_wake is unused - this should be called from
+        * the suspend() callback if device_may_wakeup(dev)) is set.
+        */
 }
 
 void __init sa1100_register_uart(int idx, int port)
index f3dfa19a1cb86eb4c8dd0b47ea8f23b8bab5f0e8..c1af04d46682657794b4893f3eac571a13acc685 100644 (file)
@@ -407,7 +407,14 @@ static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
 
 static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
-       /* todo - possibly remove AFC and do manual CTS */
+       unsigned int umcon = rd_regl(port, S3C2410_UMCON);
+
+       if (mctrl & TIOCM_RTS)
+               umcon |= S3C2410_UMCOM_RTS_LOW;
+       else
+               umcon &= ~S3C2410_UMCOM_RTS_LOW;
+
+       wr_regl(port, S3C2410_UMCON, umcon);
 }
 
 static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
@@ -774,8 +781,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
        if (termios->c_cflag & CSTOPB)
                ulcon |= S3C2410_LCON_STOPB;
 
-       umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
-
        if (termios->c_cflag & PARENB) {
                if (termios->c_cflag & PARODD)
                        ulcon |= S3C2410_LCON_PODD;
@@ -792,6 +797,15 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
 
        wr_regl(port, S3C2410_ULCON, ulcon);
        wr_regl(port, S3C2410_UBRDIV, quot);
+
+       umcon = rd_regl(port, S3C2410_UMCON);
+       if (termios->c_cflag & CRTSCTS) {
+               umcon |= S3C2410_UMCOM_AFC;
+               /* Disable RTS when RX FIFO contains 63 bytes */
+               umcon &= ~S3C2412_UMCON_AFC_8;
+       } else {
+               umcon &= ~S3C2410_UMCOM_AFC;
+       }
        wr_regl(port, S3C2410_UMCON, umcon);
 
        if (ourport->info->has_divslot)
@@ -1254,7 +1268,7 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
        ourport->baudclk = ERR_PTR(-EINVAL);
        ourport->info = ourport->drv_data->info;
        ourport->cfg = (dev_get_platdata(&pdev->dev)) ?
-                       (struct s3c2410_uartcfg *)dev_get_platdata(&pdev->dev) :
+                       dev_get_platdata(&pdev->dev) :
                        ourport->drv_data->def_cfg;
 
        ourport->port.fifosize = (ourport->info->fifosize) ?
index aaa617a6c4995e1d978ed88ab33878f232ba8b97..8827e5424cef7d13420114650bdcfa19cad91ea0 100644 (file)
@@ -63,7 +63,7 @@ struct s3c24xx_uart_port {
 
 /* conversion functions */
 
-#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
+#define s3c24xx_dev_to_port(__dev) dev_get_drvdata(__dev)
 
 /* register access controls */
 
index 49e9bbfe6cab525454c5b4337b741cbd5fcafcd0..a447f71538ef2c07d77fa92f69247559fa83ebf1 100644 (file)
@@ -986,6 +986,7 @@ static int sccnxp_probe(struct platform_device *pdev)
                return 0;
        }
 
+       uart_unregister_driver(&s->uart);
 err_out:
        if (!IS_ERR(s->regulator))
                return regulator_disable(s->regulator);
index 0489a2bdcdf9a1aff4af5dacd1d7766f9671a523..dfe79ccc4fb3c66f473604c2ac867b181e6e7e00 100644 (file)
@@ -1018,7 +1018,7 @@ static int tegra_uart_startup(struct uart_port *u)
                goto fail_hw_init;
        }
 
-       ret = request_irq(u->irq, tegra_uart_isr, IRQF_DISABLED,
+       ret = request_irq(u->irq, tegra_uart_isr, 0,
                                dev_name(u->dev), tup);
        if (ret < 0) {
                dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq);
index 440a962412da9251c10aa60ee6cf25cf2624474a..90a080b1f9ee13cd78d6647245ee91eb1c710da4 100644 (file)
@@ -1220,8 +1220,6 @@ static void pciserial_txx9_remove_one(struct pci_dev *dev)
 {
        struct uart_txx9_port *up = pci_get_drvdata(dev);
 
-       pci_set_drvdata(dev, NULL);
-
        if (up) {
                serial_txx9_unregister_port(up->port.line);
                pci_disable_device(dev);
index 61c1ad03db5b0159d6b7735838a2a0cb800d7bbd..f186a8fb8887119cd036625a129f515bb57f4210 100644 (file)
@@ -529,7 +529,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param)
        while (sirfport->rx_completed != sirfport->rx_issued) {
                sirfsoc_uart_insert_rx_buf_to_tty(sirfport,
                                        SIRFSOC_RX_DMA_BUF_SIZE);
-               sirfsoc_rx_submit_one_dma_desc(port, sirfport->rx_completed++);
+               sirfport->rx_completed++;
                sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT;
        }
        count = CIRC_CNT(sirfport->rx_dma_items[sirfport->rx_issued].xmit.head,
@@ -706,12 +706,19 @@ static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param)
 {
        struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
        struct uart_port *port = &sirfport->port;
+       struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+       struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
        unsigned long flags;
        spin_lock_irqsave(&sirfport->rx_lock, flags);
        while (sirfport->rx_completed != sirfport->rx_issued) {
                sirfsoc_uart_insert_rx_buf_to_tty(sirfport,
                                        SIRFSOC_RX_DMA_BUF_SIZE);
-               sirfsoc_rx_submit_one_dma_desc(port, sirfport->rx_completed++);
+               if (rd_regl(port, ureg->sirfsoc_int_en_reg) &
+                               uint_en->sirfsoc_rx_timeout_en)
+                       sirfsoc_rx_submit_one_dma_desc(port,
+                                       sirfport->rx_completed++);
+               else
+                       sirfport->rx_completed++;
                sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT;
        }
        spin_unlock_irqrestore(&sirfport->rx_lock, flags);
index fb8d0a0026073f4f6e14b46d56d6be7c834f45b3..b7d679c0881b7ba32567f56a95094a1e4cb98bf1 100644 (file)
@@ -368,15 +368,6 @@ struct sirfsoc_uart_register sirfsoc_uart = {
 #define SIRFSOC_UART_NR                                6
 #define SIRFSOC_PORT_TYPE                      0xa5
 
-/* Baud Rate Calculation */
-#define SIRF_MIN_SAMPLE_DIV                    0xf
-#define SIRF_MAX_SAMPLE_DIV                    0x3f
-#define SIRF_IOCLK_DIV_MAX                     0xffff
-#define SIRF_SAMPLE_DIV_SHIFT                  16
-#define SIRF_IOCLK_DIV_MASK                    0xffff
-#define SIRF_SAMPLE_DIV_MASK                   0x3f0000
-#define SIRF_BAUD_RATE_SUPPORT_NR              18
-
 /* Uart Common Use Macro*/
 #define SIRFSOC_RX_DMA_BUF_SIZE        256
 #define BYTES_TO_ALIGN(dma_addr)       ((unsigned long)(dma_addr) & 0x3)
@@ -453,9 +444,6 @@ struct sirfsoc_uart_port {
        int                             rx_issued;
 };
 
-/* Hardware Flow Control */
-#define SIRFUART_AFC_CTRL_RX_THD       0x70
-
 /* Register Access Control */
 #define portaddr(port, reg)            ((port)->membase + (reg))
 #define rd_regb(port, reg)             (__raw_readb(portaddr(port, reg)))
index 5d6136b2a04a8c488e609696952e5517d5cc412a..380fb5355cb26ae68a472a4e6097c57556d0b8e1 100644 (file)
@@ -894,7 +894,7 @@ static int sunsab_console_setup(struct console *con, char *options)
        case B115200: baud = 115200; break;
        case B230400: baud = 230400; break;
        case B460800: baud = 460800; break;
-       };
+       }
 
        /*
         * Temporary fix.
index 699cc1b5f6aa2c5c39cd89a2bb8ea5b192140015..db79b76f5c8e978e09824b719d585cf470d75d68 100644 (file)
@@ -522,7 +522,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break)
                                serio_interrupt(&up->serio, ch, 0);
 #endif
                                break;
-                       };
+                       }
                }
        } while (serial_in(up, UART_LSR) & UART_LSR_DR);
 }
index 135a1520353260e8f6665538ccce381e3036c562..45a8c6aa583797ba717ec3da79a24929a6eb425f 100644 (file)
@@ -319,7 +319,7 @@ static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up,
                                serio_interrupt(&up->serio, ch, 0);
 #endif
                        break;
-               };
+               }
        }
 }
 
@@ -897,7 +897,7 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag,
                up->curregs[R5] |= Tx8;
                up->parity_mask = 0xff;
                break;
-       };
+       }
        up->curregs[R4] &= ~0x0c;
        if (cflag & CSTOPB)
                up->curregs[R4] |= SB2;
@@ -1239,7 +1239,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
        default: case B9600: baud = 9600; break;
        case B19200: baud = 19200; break;
        case B38400: baud = 38400; break;
-       };
+       }
 
        brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
 
index 88317482b81fce4929dc2556e1b19358c6156017..2fd1e1789811941aea3c2d8de3611e916440e0a7 100644 (file)
@@ -269,7 +269,7 @@ static unsigned int qe_uart_tx_empty(struct uart_port *port)
                        return 1;
 
                bdp++;
-       };
+       }
 }
 
 /*
index 7e4150aa69c65b34c44e526c5633bf456f1abe2f..e46e9f3f19b90d34476b60a2e21aa656fc3c8fae 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Xilinx PS UART driver
  *
- * 2011 (c) Xilinx Inc.
+ * 2011 - 2013 (C) Xilinx Inc.
  *
  * This program is free software; you can redistribute it
  * and/or modify it under the terms of the GNU General Public
  *
  */
 
+#if defined(CONFIG_SERIAL_XILINX_PS_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
 #include <linux/platform_device.h>
 #include <linux/serial.h>
+#include <linux/console.h>
 #include <linux/serial_core.h>
 #include <linux/slab.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
-#include <linux/console.h>
 #include <linux/clk.h>
 #include <linux/irq.h>
 #include <linux/io.h>
 #define XUARTPS_MAJOR          0       /* use dynamic node allocation */
 #define XUARTPS_MINOR          0       /* works best with devtmpfs */
 #define XUARTPS_NR_PORTS       2
-#define XUARTPS_FIFO_SIZE      16      /* FIFO size */
+#define XUARTPS_FIFO_SIZE      64      /* FIFO size */
 #define XUARTPS_REGISTER_SPACE 0xFFF
 
 #define xuartps_readl(offset)          ioread32(port->membase + offset)
 #define xuartps_writel(val, offset)    iowrite32(val, port->membase + offset)
 
+/* Rx Trigger level */
+static int rx_trigger_level = 56;
+module_param(rx_trigger_level, uint, S_IRUGO);
+MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes");
+
+/* Rx Timeout */
+static int rx_timeout = 10;
+module_param(rx_timeout, uint, S_IRUGO);
+MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
+
 /********************************Register Map********************************/
 /** UART
  *
 #define XUARTPS_IXR_RXEMPTY    0x00000002 /* RX FIFO empty interrupt. */
 #define XUARTPS_IXR_MASK       0x00001FFF /* Valid bit mask */
 
+/* Goes in read_status_mask for break detection as the HW doesn't do it*/
+#define XUARTPS_IXR_BRK                0x80000000
+
 /** Channel Status Register
  *
  * The channel status register (CSR) is provided to enable the control logic
 #define XUARTPS_SR_TXFULL      0x00000010 /* TX FIFO full */
 #define XUARTPS_SR_RXTRIG      0x00000001 /* Rx Trigger */
 
+/* baud dividers min/max values */
+#define XUARTPS_BDIV_MIN       4
+#define XUARTPS_BDIV_MAX       255
+#define XUARTPS_CD_MAX         65535
+
 /**
  * struct xuartps - device data
- * @refclk     Reference clock
- * @aperclk    APB clock
+ * @port               Pointer to the UART port
+ * @refclk             Reference clock
+ * @aperclk            APB clock
+ * @baud               Current baud rate
+ * @clk_rate_change_nb Notifier block for clock changes
  */
 struct xuartps {
+       struct uart_port        *port;
        struct clk              *refclk;
        struct clk              *aperclk;
+       unsigned int            baud;
+       struct notifier_block   clk_rate_change_nb;
 };
+#define to_xuartps(_nb) container_of(_nb, struct xuartps, clk_rate_change_nb);
 
 /**
  * xuartps_isr - Interrupt handler
@@ -171,6 +200,23 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
         */
        isrstatus = xuartps_readl(XUARTPS_ISR_OFFSET);
 
+       /*
+        * There is no hardware break detection, so we interpret framing
+        * error with all-zeros data as a break sequence. Most of the time,
+        * there's another non-zero byte at the end of the sequence.
+        */
+
+       if (isrstatus & XUARTPS_IXR_FRAMING) {
+               while (!(xuartps_readl(XUARTPS_SR_OFFSET) &
+                                       XUARTPS_SR_RXEMPTY)) {
+                       if (!xuartps_readl(XUARTPS_FIFO_OFFSET)) {
+                               port->read_status_mask |= XUARTPS_IXR_BRK;
+                               isrstatus &= ~XUARTPS_IXR_FRAMING;
+                       }
+               }
+               xuartps_writel(XUARTPS_IXR_FRAMING, XUARTPS_ISR_OFFSET);
+       }
+
        /* drop byte with parity error if IGNPAR specified */
        if (isrstatus & port->ignore_status_mask & XUARTPS_IXR_PARITY)
                isrstatus &= ~(XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT);
@@ -184,6 +230,30 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
                while ((xuartps_readl(XUARTPS_SR_OFFSET) &
                        XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) {
                        data = xuartps_readl(XUARTPS_FIFO_OFFSET);
+
+                       /* Non-NULL byte after BREAK is garbage (99%) */
+                       if (data && (port->read_status_mask &
+                                               XUARTPS_IXR_BRK)) {
+                               port->read_status_mask &= ~XUARTPS_IXR_BRK;
+                               port->icount.brk++;
+                               if (uart_handle_break(port))
+                                       continue;
+                       }
+
+                       /*
+                        * uart_handle_sysrq_char() doesn't work if
+                        * spinlocked, for some reason
+                        */
+                        if (port->sysrq) {
+                               spin_unlock(&port->lock);
+                               if (uart_handle_sysrq_char(port,
+                                                       (unsigned char)data)) {
+                                       spin_lock(&port->lock);
+                                       continue;
+                               }
+                               spin_lock(&port->lock);
+                       }
+
                        port->icount.rx++;
 
                        if (isrstatus & XUARTPS_IXR_PARITY) {
@@ -247,63 +317,196 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
 }
 
 /**
- * xuartps_set_baud_rate - Calculate and set the baud rate
- * @port: Handle to the uart port structure
- * @baud: Baud rate to set
- *
+ * xuartps_calc_baud_divs - Calculate baud rate divisors
+ * @clk: UART module input clock
+ * @baud: Desired baud rate
+ * @rbdiv: BDIV value (return value)
+ * @rcd: CD value (return value)
+ * @div8: Value for clk_sel bit in mod (return value)
  * Returns baud rate, requested baud when possible, or actual baud when there
- *     was too much error
- **/
-static unsigned int xuartps_set_baud_rate(struct uart_port *port,
-                                               unsigned int baud)
+ *     was too much error, zero if no valid divisors are found.
+ *
+ * Formula to obtain baud rate is
+ *     baud_tx/rx rate = clk/CD * (BDIV + 1)
+ *     input_clk = (Uart User Defined Clock or Apb Clock)
+ *             depends on UCLKEN in MR Reg
+ *     clk = input_clk or input_clk/8;
+ *             depends on CLKS in MR reg
+ *     CD and BDIV depends on values in
+ *                     baud rate generate register
+ *                     baud rate clock divisor register
+ */
+static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud,
+               u32 *rbdiv, u32 *rcd, int *div8)
 {
-       unsigned int sel_clk;
-       unsigned int calc_baud = 0;
-       unsigned int brgr_val, brdiv_val;
+       u32 cd, bdiv;
+       unsigned int calc_baud;
+       unsigned int bestbaud = 0;
        unsigned int bauderror;
+       unsigned int besterror = ~0;
 
-       /* Formula to obtain baud rate is
-        *      baud_tx/rx rate = sel_clk/CD * (BDIV + 1)
-        *      input_clk = (Uart User Defined Clock or Apb Clock)
-        *              depends on UCLKEN in MR Reg
-        *      sel_clk = input_clk or input_clk/8;
-        *              depends on CLKS in MR reg
-        *      CD and BDIV depends on values in
-        *                      baud rate generate register
-        *                      baud rate clock divisor register
-        */
-       sel_clk = port->uartclk;
-       if (xuartps_readl(XUARTPS_MR_OFFSET) & XUARTPS_MR_CLKSEL)
-               sel_clk = sel_clk / 8;
-
-       /* Find the best values for baud generation */
-       for (brdiv_val = 4; brdiv_val < 255; brdiv_val++) {
+       if (baud < clk / ((XUARTPS_BDIV_MAX + 1) * XUARTPS_CD_MAX)) {
+               *div8 = 1;
+               clk /= 8;
+       } else {
+               *div8 = 0;
+       }
 
-               brgr_val = sel_clk / (baud * (brdiv_val + 1));
-               if (brgr_val < 2 || brgr_val > 65535)
+       for (bdiv = XUARTPS_BDIV_MIN; bdiv <= XUARTPS_BDIV_MAX; bdiv++) {
+               cd = DIV_ROUND_CLOSEST(clk, baud * (bdiv + 1));
+               if (cd < 1 || cd > XUARTPS_CD_MAX)
                        continue;
 
-               calc_baud = sel_clk / (brgr_val * (brdiv_val + 1));
+               calc_baud = clk / (cd * (bdiv + 1));
 
                if (baud > calc_baud)
                        bauderror = baud - calc_baud;
                else
                        bauderror = calc_baud - baud;
 
-               /* use the values when percent error is acceptable */
-               if (((bauderror * 100) / baud) < 3) {
-                       calc_baud = baud;
-                       break;
+               if (besterror > bauderror) {
+                       *rbdiv = bdiv;
+                       *rcd = cd;
+                       bestbaud = calc_baud;
+                       besterror = bauderror;
                }
        }
+       /* use the values when percent error is acceptable */
+       if (((besterror * 100) / baud) < 3)
+               bestbaud = baud;
+
+       return bestbaud;
+}
 
-       /* Set the values for the new baud rate */
-       xuartps_writel(brgr_val, XUARTPS_BAUDGEN_OFFSET);
-       xuartps_writel(brdiv_val, XUARTPS_BAUDDIV_OFFSET);
+/**
+ * xuartps_set_baud_rate - Calculate and set the baud rate
+ * @port: Handle to the uart port structure
+ * @baud: Baud rate to set
+ * Returns baud rate, requested baud when possible, or actual baud when there
+ *        was too much error, zero if no valid divisors are found.
+ */
+static unsigned int xuartps_set_baud_rate(struct uart_port *port,
+               unsigned int baud)
+{
+       unsigned int calc_baud;
+       u32 cd = 0, bdiv = 0;
+       u32 mreg;
+       int div8;
+       struct xuartps *xuartps = port->private_data;
+
+       calc_baud = xuartps_calc_baud_divs(port->uartclk, baud, &bdiv, &cd,
+                       &div8);
+
+       /* Write new divisors to hardware */
+       mreg = xuartps_readl(XUARTPS_MR_OFFSET);
+       if (div8)
+               mreg |= XUARTPS_MR_CLKSEL;
+       else
+               mreg &= ~XUARTPS_MR_CLKSEL;
+       xuartps_writel(mreg, XUARTPS_MR_OFFSET);
+       xuartps_writel(cd, XUARTPS_BAUDGEN_OFFSET);
+       xuartps_writel(bdiv, XUARTPS_BAUDDIV_OFFSET);
+       xuartps->baud = baud;
 
        return calc_baud;
 }
 
+#ifdef CONFIG_COMMON_CLK
+/**
+ * xuartps_clk_notitifer_cb - Clock notifier callback
+ * @nb:                Notifier block
+ * @event:     Notify event
+ * @data:      Notifier data
+ * Returns NOTIFY_OK on success, NOTIFY_BAD on error.
+ */
+static int xuartps_clk_notifier_cb(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       u32 ctrl_reg;
+       struct uart_port *port;
+       int locked = 0;
+       struct clk_notifier_data *ndata = data;
+       unsigned long flags = 0;
+       struct xuartps *xuartps = to_xuartps(nb);
+
+       port = xuartps->port;
+       if (port->suspended)
+               return NOTIFY_OK;
+
+       switch (event) {
+       case PRE_RATE_CHANGE:
+       {
+               u32 bdiv;
+               u32 cd;
+               int div8;
+
+               /*
+                * Find out if current baud-rate can be achieved with new clock
+                * frequency.
+                */
+               if (!xuartps_calc_baud_divs(ndata->new_rate, xuartps->baud,
+                                       &bdiv, &cd, &div8))
+                       return NOTIFY_BAD;
+
+               spin_lock_irqsave(&xuartps->port->lock, flags);
+
+               /* Disable the TX and RX to set baud rate */
+               xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) |
+                               (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS),
+                               XUARTPS_CR_OFFSET);
+
+               spin_unlock_irqrestore(&xuartps->port->lock, flags);
+
+               return NOTIFY_OK;
+       }
+       case POST_RATE_CHANGE:
+               /*
+                * Set clk dividers to generate correct baud with new clock
+                * frequency.
+                */
+
+               spin_lock_irqsave(&xuartps->port->lock, flags);
+
+               locked = 1;
+               port->uartclk = ndata->new_rate;
+
+               xuartps->baud = xuartps_set_baud_rate(xuartps->port,
+                               xuartps->baud);
+               /* fall through */
+       case ABORT_RATE_CHANGE:
+               if (!locked)
+                       spin_lock_irqsave(&xuartps->port->lock, flags);
+
+               /* Set TX/RX Reset */
+               xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) |
+                               (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST),
+                               XUARTPS_CR_OFFSET);
+
+               while (xuartps_readl(XUARTPS_CR_OFFSET) &
+                               (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST))
+                       cpu_relax();
+
+               /*
+                * Clear the RX disable and TX disable bits and then set the TX
+                * enable bit and RX enable bit to enable the transmitter and
+                * receiver.
+                */
+               xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET);
+               ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET);
+               xuartps_writel(
+                       (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) |
+                       (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN),
+                       XUARTPS_CR_OFFSET);
+
+               spin_unlock_irqrestore(&xuartps->port->lock, flags);
+
+               return NOTIFY_OK;
+       default:
+               return NOTIFY_DONE;
+       }
+}
+#endif
+
 /*----------------------Uart Operations---------------------------*/
 
 /**
@@ -346,7 +549,7 @@ static void xuartps_start_tx(struct uart_port *port)
                port->state->xmit.tail = (port->state->xmit.tail + 1) &
                                        (UART_XMIT_SIZE - 1);
        }
-
+       xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_ISR_OFFSET);
        /* Enable the TX Empty interrupt */
        xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_IER_OFFSET);
 
@@ -437,7 +640,7 @@ static void xuartps_set_termios(struct uart_port *port,
                                struct ktermios *termios, struct ktermios *old)
 {
        unsigned int cval = 0;
-       unsigned int baud;
+       unsigned int baud, minbaud, maxbaud;
        unsigned long flags;
        unsigned int ctrl_reg, mode_reg;
 
@@ -454,8 +657,14 @@ static void xuartps_set_termios(struct uart_port *port,
                        (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS),
                        XUARTPS_CR_OFFSET);
 
-       /* Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk */
-       baud = uart_get_baud_rate(port, termios, old, 0, 10000000);
+       /*
+        * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk
+        * min and max baud should be calculated here based on port->uartclk.
+        * this way we get a valid baud and can safely call set_baud()
+        */
+       minbaud = port->uartclk / ((XUARTPS_BDIV_MAX + 1) * XUARTPS_CD_MAX * 8);
+       maxbaud = port->uartclk / (XUARTPS_BDIV_MIN + 1);
+       baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud);
        baud = xuartps_set_baud_rate(port, baud);
        if (tty_termios_baud_rate(termios))
                tty_termios_encode_baud_rate(termios, baud, baud);
@@ -480,7 +689,7 @@ static void xuartps_set_termios(struct uart_port *port,
                        | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN),
                        XUARTPS_CR_OFFSET);
 
-       xuartps_writel(10, XUARTPS_RXTOUT_OFFSET);
+       xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET);
 
        port->read_status_mask = XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXTRIG |
                        XUARTPS_IXR_OVERRUN | XUARTPS_IXR_TOUT;
@@ -531,13 +740,17 @@ static void xuartps_set_termios(struct uart_port *port,
                                cval |= XUARTPS_MR_PARITY_MARK;
                        else
                                cval |= XUARTPS_MR_PARITY_SPACE;
-               } else if (termios->c_cflag & PARODD)
+               } else {
+                       if (termios->c_cflag & PARODD)
                                cval |= XUARTPS_MR_PARITY_ODD;
                        else
                                cval |= XUARTPS_MR_PARITY_EVEN;
-       } else
+               }
+       } else {
                cval |= XUARTPS_MR_PARITY_NONE;
-       xuartps_writel(cval , XUARTPS_MR_OFFSET);
+       }
+       cval |= mode_reg & 1;
+       xuartps_writel(cval, XUARTPS_MR_OFFSET);
 
        spin_unlock_irqrestore(&port->lock, flags);
 }
@@ -583,11 +796,17 @@ static int xuartps_startup(struct uart_port *port)
                | XUARTPS_MR_PARITY_NONE | XUARTPS_MR_CHARLEN_8_BIT,
                 XUARTPS_MR_OFFSET);
 
-       /* Set the RX FIFO Trigger level to 14 assuming FIFO size as 16 */
-       xuartps_writel(14, XUARTPS_RXWM_OFFSET);
+       /*
+        * Set the RX FIFO Trigger level to use most of the FIFO, but it
+        * can be tuned with a module parameter
+        */
+       xuartps_writel(rx_trigger_level, XUARTPS_RXWM_OFFSET);
 
-       /* Receive Timeout register is enabled with value of 10 */
-       xuartps_writel(10, XUARTPS_RXTOUT_OFFSET);
+       /*
+        * Receive Timeout register is enabled but it
+        * can be tuned with a module parameter
+        */
+       xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET);
 
        /* Clear out any pending interrupts before enabling them */
        xuartps_writel(xuartps_readl(XUARTPS_ISR_OFFSET), XUARTPS_ISR_OFFSET);
@@ -727,6 +946,54 @@ static void xuartps_enable_ms(struct uart_port *port)
        /* N/A */
 }
 
+#ifdef CONFIG_CONSOLE_POLL
+static int xuartps_poll_get_char(struct uart_port *port)
+{
+       u32 imr;
+       int c;
+
+       /* Disable all interrupts */
+       imr = xuartps_readl(XUARTPS_IMR_OFFSET);
+       xuartps_writel(imr, XUARTPS_IDR_OFFSET);
+
+       /* Check if FIFO is empty */
+       if (xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY)
+               c = NO_POLL_CHAR;
+       else /* Read a character */
+               c = (unsigned char) xuartps_readl(XUARTPS_FIFO_OFFSET);
+
+       /* Enable interrupts */
+       xuartps_writel(imr, XUARTPS_IER_OFFSET);
+
+       return c;
+}
+
+static void xuartps_poll_put_char(struct uart_port *port, unsigned char c)
+{
+       u32 imr;
+
+       /* Disable all interrupts */
+       imr = xuartps_readl(XUARTPS_IMR_OFFSET);
+       xuartps_writel(imr, XUARTPS_IDR_OFFSET);
+
+       /* Wait until FIFO is empty */
+       while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY))
+               cpu_relax();
+
+       /* Write a character */
+       xuartps_writel(c, XUARTPS_FIFO_OFFSET);
+
+       /* Wait until FIFO is empty */
+       while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY))
+               cpu_relax();
+
+       /* Enable interrupts */
+       xuartps_writel(imr, XUARTPS_IER_OFFSET);
+
+       return;
+}
+#endif
+
 /** The UART operations structure
  */
 static struct uart_ops xuartps_ops = {
@@ -759,6 +1026,10 @@ static struct uart_ops xuartps_ops = {
        .config_port    = xuartps_config_port,  /* Configure when driver
                                                 * adds a xuartps port
                                                 */
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_get_char  = xuartps_poll_get_char,
+       .poll_put_char  = xuartps_poll_put_char,
+#endif
 };
 
 static struct uart_port xuartps_port[2];
@@ -837,7 +1108,7 @@ static void xuartps_console_write(struct console *co, const char *s,
 {
        struct uart_port *port = &xuartps_port[co->index];
        unsigned long flags;
-       unsigned int imr;
+       unsigned int imr, ctrl;
        int locked = 1;
 
        if (oops_in_progress)
@@ -849,9 +1120,19 @@ static void xuartps_console_write(struct console *co, const char *s,
        imr = xuartps_readl(XUARTPS_IMR_OFFSET);
        xuartps_writel(imr, XUARTPS_IDR_OFFSET);
 
+       /*
+        * Make sure that the tx part is enabled. Set the TX enable bit and
+        * clear the TX disable bit to enable the transmitter.
+        */
+       ctrl = xuartps_readl(XUARTPS_CR_OFFSET);
+       xuartps_writel((ctrl & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN,
+               XUARTPS_CR_OFFSET);
+
        uart_console_write(port, s, count, xuartps_console_putchar);
        xuartps_console_wait_tx(port);
 
+       xuartps_writel(ctrl, XUARTPS_CR_OFFSET);
+
        /* restore interrupt state, it seems like there may be a h/w bug
         * in that the interrupt enable register should not need to be
         * written based on the data sheet
@@ -933,6 +1214,119 @@ static struct uart_driver xuartps_uart_driver = {
 #endif
 };
 
+#ifdef CONFIG_PM_SLEEP
+/**
+ * xuartps_suspend - suspend event
+ * @device: Pointer to the device structure
+ *
+ * Returns 0
+ */
+static int xuartps_suspend(struct device *device)
+{
+       struct uart_port *port = dev_get_drvdata(device);
+       struct tty_struct *tty;
+       struct device *tty_dev;
+       int may_wake = 0;
+
+       /* Get the tty which could be NULL so don't assume it's valid */
+       tty = tty_port_tty_get(&port->state->port);
+       if (tty) {
+               tty_dev = tty->dev;
+               may_wake = device_may_wakeup(tty_dev);
+               tty_kref_put(tty);
+       }
+
+       /*
+        * Call the API provided in serial_core.c file which handles
+        * the suspend.
+        */
+       uart_suspend_port(&xuartps_uart_driver, port);
+       if (console_suspend_enabled && !may_wake) {
+               struct xuartps *xuartps = port->private_data;
+
+               clk_disable(xuartps->refclk);
+               clk_disable(xuartps->aperclk);
+       } else {
+               unsigned long flags = 0;
+
+               spin_lock_irqsave(&port->lock, flags);
+               /* Empty the receive FIFO 1st before making changes */
+               while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY))
+                       xuartps_readl(XUARTPS_FIFO_OFFSET);
+               /* set RX trigger level to 1 */
+               xuartps_writel(1, XUARTPS_RXWM_OFFSET);
+               /* disable RX timeout interrups */
+               xuartps_writel(XUARTPS_IXR_TOUT, XUARTPS_IDR_OFFSET);
+               spin_unlock_irqrestore(&port->lock, flags);
+       }
+
+       return 0;
+}
+
+/**
+ * xuartps_resume - Resume after a previous suspend
+ * @device: Pointer to the device structure
+ *
+ * Returns 0
+ */
+static int xuartps_resume(struct device *device)
+{
+       struct uart_port *port = dev_get_drvdata(device);
+       unsigned long flags = 0;
+       u32 ctrl_reg;
+       struct tty_struct *tty;
+       struct device *tty_dev;
+       int may_wake = 0;
+
+       /* Get the tty which could be NULL so don't assume it's valid */
+       tty = tty_port_tty_get(&port->state->port);
+       if (tty) {
+               tty_dev = tty->dev;
+               may_wake = device_may_wakeup(tty_dev);
+               tty_kref_put(tty);
+       }
+
+       if (console_suspend_enabled && !may_wake) {
+               struct xuartps *xuartps = port->private_data;
+
+               clk_enable(xuartps->aperclk);
+               clk_enable(xuartps->refclk);
+
+               spin_lock_irqsave(&port->lock, flags);
+
+               /* Set TX/RX Reset */
+               xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) |
+                               (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST),
+                               XUARTPS_CR_OFFSET);
+               while (xuartps_readl(XUARTPS_CR_OFFSET) &
+                               (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST))
+                       cpu_relax();
+
+               /* restore rx timeout value */
+               xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET);
+               /* Enable Tx/Rx */
+               ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET);
+               xuartps_writel(
+                       (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) |
+                       (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN),
+                       XUARTPS_CR_OFFSET);
+
+               spin_unlock_irqrestore(&port->lock, flags);
+       } else {
+               spin_lock_irqsave(&port->lock, flags);
+               /* restore original rx trigger level */
+               xuartps_writel(rx_trigger_level, XUARTPS_RXWM_OFFSET);
+               /* enable RX timeout interrupt */
+               xuartps_writel(XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET);
+               spin_unlock_irqrestore(&port->lock, flags);
+       }
+
+       return uart_resume_port(&xuartps_uart_driver, port);
+}
+#endif /* ! CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(xuartps_dev_pm_ops, xuartps_suspend, xuartps_resume);
+
 /* ---------------------------------------------------------------------
  * Platform bus binding
  */
@@ -949,27 +1343,26 @@ static int xuartps_probe(struct platform_device *pdev)
        struct resource *res, *res2;
        struct xuartps *xuartps_data;
 
-       xuartps_data = kzalloc(sizeof(*xuartps_data), GFP_KERNEL);
+       xuartps_data = devm_kzalloc(&pdev->dev, sizeof(*xuartps_data),
+                       GFP_KERNEL);
        if (!xuartps_data)
                return -ENOMEM;
 
-       xuartps_data->aperclk = clk_get(&pdev->dev, "aper_clk");
+       xuartps_data->aperclk = devm_clk_get(&pdev->dev, "aper_clk");
        if (IS_ERR(xuartps_data->aperclk)) {
                dev_err(&pdev->dev, "aper_clk clock not found.\n");
-               rc = PTR_ERR(xuartps_data->aperclk);
-               goto err_out_free;
+               return PTR_ERR(xuartps_data->aperclk);
        }
-       xuartps_data->refclk = clk_get(&pdev->dev, "ref_clk");
+       xuartps_data->refclk = devm_clk_get(&pdev->dev, "ref_clk");
        if (IS_ERR(xuartps_data->refclk)) {
                dev_err(&pdev->dev, "ref_clk clock not found.\n");
-               rc = PTR_ERR(xuartps_data->refclk);
-               goto err_out_clk_put_aper;
+               return PTR_ERR(xuartps_data->refclk);
        }
 
        rc = clk_prepare_enable(xuartps_data->aperclk);
        if (rc) {
                dev_err(&pdev->dev, "Unable to enable APER clock.\n");
-               goto err_out_clk_put;
+               return rc;
        }
        rc = clk_prepare_enable(xuartps_data->refclk);
        if (rc) {
@@ -989,13 +1382,21 @@ static int xuartps_probe(struct platform_device *pdev)
                goto err_out_clk_disable;
        }
 
+#ifdef CONFIG_COMMON_CLK
+       xuartps_data->clk_rate_change_nb.notifier_call =
+                       xuartps_clk_notifier_cb;
+       if (clk_notifier_register(xuartps_data->refclk,
+                               &xuartps_data->clk_rate_change_nb))
+               dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
+#endif
+
        /* Initialize the port structure */
        port = xuartps_get_port();
 
        if (!port) {
                dev_err(&pdev->dev, "Cannot get uart_port structure\n");
                rc = -ENODEV;
-               goto err_out_clk_disable;
+               goto err_out_notif_unreg;
        } else {
                /* Register the port.
                 * This function also registers this device with the tty layer
@@ -1006,26 +1407,26 @@ static int xuartps_probe(struct platform_device *pdev)
                port->dev = &pdev->dev;
                port->uartclk = clk_get_rate(xuartps_data->refclk);
                port->private_data = xuartps_data;
+               xuartps_data->port = port;
                platform_set_drvdata(pdev, port);
                rc = uart_add_one_port(&xuartps_uart_driver, port);
                if (rc) {
                        dev_err(&pdev->dev,
                                "uart_add_one_port() failed; err=%i\n", rc);
-                       goto err_out_clk_disable;
+                       goto err_out_notif_unreg;
                }
                return 0;
        }
 
+err_out_notif_unreg:
+#ifdef CONFIG_COMMON_CLK
+       clk_notifier_unregister(xuartps_data->refclk,
+                       &xuartps_data->clk_rate_change_nb);
+#endif
 err_out_clk_disable:
        clk_disable_unprepare(xuartps_data->refclk);
 err_out_clk_dis_aper:
        clk_disable_unprepare(xuartps_data->aperclk);
-err_out_clk_put:
-       clk_put(xuartps_data->refclk);
-err_out_clk_put_aper:
-       clk_put(xuartps_data->aperclk);
-err_out_free:
-       kfree(xuartps_data);
 
        return rc;
 }
@@ -1043,13 +1444,14 @@ static int xuartps_remove(struct platform_device *pdev)
        int rc;
 
        /* Remove the xuartps port from the serial core */
+#ifdef CONFIG_COMMON_CLK
+       clk_notifier_unregister(xuartps_data->refclk,
+                       &xuartps_data->clk_rate_change_nb);
+#endif
        rc = uart_remove_one_port(&xuartps_uart_driver, port);
        port->mapbase = 0;
        clk_disable_unprepare(xuartps_data->refclk);
        clk_disable_unprepare(xuartps_data->aperclk);
-       clk_put(xuartps_data->refclk);
-       clk_put(xuartps_data->aperclk);
-       kfree(xuartps_data);
        return rc;
 }
 
@@ -1067,6 +1469,7 @@ static struct platform_driver xuartps_platform_driver = {
                .owner = THIS_MODULE,
                .name = XUARTPS_NAME,           /* Driver name */
                .of_match_table = xuartps_of_match,
+               .pm = &xuartps_dev_pm_ops,
                },
 };
 
index 40a9fe9d3b10f0170af31c4d8845d8cc609e6bb5..ce396ecdf41268b5de64a7ac3f820438c3bdfffb 100644 (file)
@@ -51,7 +51,7 @@
 #include <asm/irq_regs.h>
 
 /* Whether we react on sysrq keys or just ignore them */
-static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
+static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE;
 static bool __read_mostly sysrq_always_enabled;
 
 unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
index f597e88a705d1fa8e65cf36ae98a61f84f20b9a3..c94d2349dd0620dd183645ee37ddced4551e9ccd 100644 (file)
@@ -140,6 +140,10 @@ EXPORT_SYMBOL(tty_port_destroy);
 static void tty_port_destructor(struct kref *kref)
 {
        struct tty_port *port = container_of(kref, struct tty_port, kref);
+
+       /* check if last port ref was dropped before tty release */
+       if (WARN_ON(port->itty))
+               return;
        if (port->xmit_buf)
                free_page((unsigned long)port->xmit_buf);
        tty_port_destroy(port);
@@ -480,8 +484,6 @@ int tty_port_close_start(struct tty_port *port,
 
        if (port->count) {
                spin_unlock_irqrestore(&port->lock, flags);
-               if (port->ops->drop)
-                       port->ops->drop(port);
                return 0;
        }
        set_bit(ASYNCB_CLOSING, &port->flags);
@@ -500,9 +502,7 @@ int tty_port_close_start(struct tty_port *port,
        /* Flush the ldisc buffering */
        tty_ldisc_flush(tty);
 
-       /* Don't call port->drop for the last reference. Callers will want
-          to drop the last active reference in ->shutdown() or the tty
-          shutdown path */
+       /* Report to caller this is the last port reference */
        return 1;
 }
 EXPORT_SYMBOL(tty_port_close_start);
index 9a8e8c5a0c73a93420fc71b85637db4d510ae241..61b1137d7e56d877fad8b2339c368cd09a5419e1 100644 (file)
@@ -1300,21 +1300,30 @@ static void csi_m(struct vc_data *vc)
                        case 27:
                                vc->vc_reverse = 0;
                                break;
-                       case 38: /* ANSI X3.64-1979 (SCO-ish?)
-                                 * Enables underscore, white foreground
-                                 * with white underscore (Linux - use
-                                 * default foreground).
+                       case 38:
+                       case 48: /* ITU T.416
+                                 * Higher colour modes.
+                                 * They break the usual properties of SGR codes
+                                 * and thus need to be detected and ignored by
+                                 * hand.  Strictly speaking, that standard also
+                                 * wants : rather than ; as separators, contrary
+                                 * to ECMA-48, but no one produces such codes
+                                 * and almost no one accepts them.
                                  */
-                               vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0);
-                               vc->vc_underline = 1;
+                               i++;
+                               if (i > vc->vc_npar)
+                                       break;
+                               if (vc->vc_par[i] == 5)      /* 256 colours */
+                                       i++;                 /* ubiquitous */
+                               else if (vc->vc_par[i] == 2) /* 24 bit colours */
+                                       i += 3;              /* extremely rare */
+                               /* Subcommands 3 (CMY) and 4 (CMYK) are so insane
+                                * that detecting them is not worth the few extra
+                                * bytes of kernel's size.
+                                */
                                break;
-                       case 39: /* ANSI X3.64-1979 (SCO-ish?)
-                                 * Disable underline option.
-                                 * Reset colour to default? It did this
-                                 * before...
-                                 */
+                       case 39:
                                vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0);
-                               vc->vc_underline = 0;
                                break;
                        case 49:
                                vc->vc_color = (vc->vc_def_color & 0xf0) | (vc->vc_color & 0x0f);
index ba475632c5fa2aea166efa3b03985139e071db2f..67beb84449304d987c68a544c02b9c6e428dce1a 100644 (file)
@@ -288,13 +288,13 @@ static int uio_dev_add_attributes(struct uio_device *idev)
                }
                map = kzalloc(sizeof(*map), GFP_KERNEL);
                if (!map)
-                       goto err_map;
+                       goto err_map_kobj;
                kobject_init(&map->kobj, &map_attr_type);
                map->mem = mem;
                mem->map = map;
                ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
                if (ret)
-                       goto err_map;
+                       goto err_map_kobj;
                ret = kobject_uevent(&map->kobj, KOBJ_ADD);
                if (ret)
                        goto err_map;
@@ -313,14 +313,14 @@ static int uio_dev_add_attributes(struct uio_device *idev)
                }
                portio = kzalloc(sizeof(*portio), GFP_KERNEL);
                if (!portio)
-                       goto err_portio;
+                       goto err_portio_kobj;
                kobject_init(&portio->kobj, &portio_attr_type);
                portio->port = port;
                port->portio = portio;
                ret = kobject_add(&portio->kobj, idev->portio_dir,
                                                        "port%d", pi);
                if (ret)
-                       goto err_portio;
+                       goto err_portio_kobj;
                ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
                if (ret)
                        goto err_portio;
@@ -329,14 +329,18 @@ static int uio_dev_add_attributes(struct uio_device *idev)
        return 0;
 
 err_portio:
-       for (pi--; pi >= 0; pi--) {
+       pi--;
+err_portio_kobj:
+       for (; pi >= 0; pi--) {
                port = &idev->info->port[pi];
                portio = port->portio;
                kobject_put(&portio->kobj);
        }
        kobject_put(idev->portio_dir);
 err_map:
-       for (mi--; mi>=0; mi--) {
+       mi--;
+err_map_kobj:
+       for (; mi >= 0; mi--) {
                mem = &idev->info->mem[mi];
                map = mem->map;
                kobject_put(&map->kobj);
@@ -601,6 +605,7 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        struct uio_device *idev = vma->vm_private_data;
        struct page *page;
        unsigned long offset;
+       void *addr;
 
        int mi = uio_find_mem_index(vma);
        if (mi < 0)
@@ -612,10 +617,11 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
         */
        offset = (vmf->pgoff - mi) << PAGE_SHIFT;
 
+       addr = (void *)(unsigned long)idev->info->mem[mi].addr + offset;
        if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL)
-               page = virt_to_page(idev->info->mem[mi].addr + offset);
+               page = virt_to_page(addr);
        else
-               page = vmalloc_to_page((void *)(unsigned long)idev->info->mem[mi].addr + offset);
+               page = vmalloc_to_page(addr);
        get_page(page);
        vmf->page = page;
        return 0;
@@ -642,16 +648,29 @@ static int uio_mmap_physical(struct vm_area_struct *vma)
 {
        struct uio_device *idev = vma->vm_private_data;
        int mi = uio_find_mem_index(vma);
+       struct uio_mem *mem;
        if (mi < 0)
                return -EINVAL;
+       mem = idev->info->mem + mi;
 
-       vma->vm_ops = &uio_physical_vm_ops;
+       if (vma->vm_end - vma->vm_start > mem->size)
+               return -EINVAL;
 
+       vma->vm_ops = &uio_physical_vm_ops;
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 
+       /*
+        * We cannot use the vm_iomap_memory() helper here,
+        * because vma->vm_pgoff is the map index we looked
+        * up above in uio_find_mem_index(), rather than an
+        * actual page offset into the mmap.
+        *
+        * So we just do the physical mmap without a page
+        * offset.
+        */
        return remap_pfn_range(vma,
                               vma->vm_start,
-                              idev->info->mem[mi].addr >> PAGE_SHIFT,
+                              mem->addr >> PAGE_SHIFT,
                               vma->vm_end - vma->vm_start,
                               vma->vm_page_prot);
 }
@@ -796,10 +815,9 @@ int __uio_register_device(struct module *owner,
 
        info->uio_dev = NULL;
 
-       idev = kzalloc(sizeof(*idev), GFP_KERNEL);
+       idev = devm_kzalloc(parent, sizeof(*idev), GFP_KERNEL);
        if (!idev) {
-               ret = -ENOMEM;
-               goto err_kzalloc;
+               return -ENOMEM;
        }
 
        idev->owner = owner;
@@ -809,7 +827,7 @@ int __uio_register_device(struct module *owner,
 
        ret = uio_get_minor(idev);
        if (ret)
-               goto err_get_minor;
+               return ret;
 
        idev->dev = device_create(&uio_class, parent,
                                  MKDEV(uio_major, idev->minor), idev,
@@ -827,7 +845,7 @@ int __uio_register_device(struct module *owner,
        info->uio_dev = idev;
 
        if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
-               ret = request_irq(info->irq, uio_interrupt,
+               ret = devm_request_irq(parent, info->irq, uio_interrupt,
                                  info->irq_flags, info->name, idev);
                if (ret)
                        goto err_request_irq;
@@ -841,9 +859,6 @@ err_uio_dev_add_attributes:
        device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
 err_device_create:
        uio_free_minor(idev);
-err_get_minor:
-       kfree(idev);
-err_kzalloc:
        return ret;
 }
 EXPORT_SYMBOL_GPL(__uio_register_device);
@@ -864,13 +879,9 @@ void uio_unregister_device(struct uio_info *info)
 
        uio_free_minor(idev);
 
-       if (info->irq && (info->irq != UIO_IRQ_CUSTOM))
-               free_irq(info->irq, idev);
-
        uio_dev_del_attributes(idev);
 
        device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
-       kfree(idev);
 
        return;
 }
index f3611c2d83b681e1903d9bc502347108f50263bc..1549fab633c6a6790dd9aeb2dacdc02ee61f4d9f 100644 (file)
@@ -147,7 +147,6 @@ static void remove(struct pci_dev *pdev)
        uio_unregister_device(info);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        iounmap(info->priv);
 
        kfree(info);
index 22cdf385ab33159cc99c37fd10946f96bbefdf85..30f533ce37585ed6eb7f82c56c91830442b2809a 100644 (file)
@@ -106,7 +106,6 @@ static void hilscher_pci_remove(struct pci_dev *dev)
        uio_unregister_device(info);
        pci_release_regions(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
        iounmap(info->mem[0].internal_addr);
 
        kfree (info);
index a1768b2f449395b1f58220cbb649bc76ee5d42ce..f764adbfe036861dfccbada4ac9f27152e2f88aa 100644 (file)
@@ -42,7 +42,7 @@
 
 enum mf624_interrupt_source {ADC, CTR4, ALL};
 
-void mf624_disable_interrupt(enum mf624_interrupt_source source,
+static void mf624_disable_interrupt(enum mf624_interrupt_source source,
                             struct uio_info *info)
 {
        void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
@@ -70,7 +70,7 @@ void mf624_disable_interrupt(enum mf624_interrupt_source source,
        }
 }
 
-void mf624_enable_interrupt(enum mf624_interrupt_source source,
+static void mf624_enable_interrupt(enum mf624_interrupt_source source,
                            struct uio_info *info)
 {
        void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
@@ -220,7 +220,6 @@ static void mf624_pci_remove(struct pci_dev *dev)
        uio_unregister_device(info);
        pci_release_regions(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
 
        iounmap(info->mem[0].internal_addr);
        iounmap(info->mem[1].internal_addr);
index 28a766b9e198af4ded7bc8ddfe2c28181909ddea..4c345db8b016e8adab57c49e2f19992113ec41b8 100644 (file)
@@ -127,7 +127,6 @@ static void netx_pci_remove(struct pci_dev *dev)
        uio_unregister_device(info);
        pci_release_regions(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
        iounmap(info->mem[0].internal_addr);
 
        kfree(info);
index 90ff17a0202fb17e090985b7798848213aa2acf6..76669313e9a705679af830b23b2d2d8f48a30e4e 100644 (file)
@@ -112,11 +112,11 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
 
        if (pdev->dev.of_node) {
                /* alloc uioinfo for one device */
-               uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
+               uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo),
+                                      GFP_KERNEL);
                if (!uioinfo) {
-                       ret = -ENOMEM;
                        dev_err(&pdev->dev, "unable to kmalloc\n");
-                       return ret;
+                       return -ENOMEM;
                }
                uioinfo->name = pdev->dev.of_node->name;
                uioinfo->version = "devicetree";
@@ -125,20 +125,19 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
 
        if (!uioinfo || !uioinfo->name || !uioinfo->version) {
                dev_err(&pdev->dev, "missing platform_data\n");
-               goto bad0;
+               return ret;
        }
 
        if (uioinfo->handler || uioinfo->irqcontrol ||
            uioinfo->irq_flags & IRQF_SHARED) {
                dev_err(&pdev->dev, "interrupt configuration error\n");
-               goto bad0;
+               return ret;
        }
 
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv) {
-               ret = -ENOMEM;
                dev_err(&pdev->dev, "unable to kmalloc\n");
-               goto bad0;
+               return -ENOMEM;
        }
 
        priv->uioinfo = uioinfo;
@@ -153,7 +152,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
                        uioinfo->irq = UIO_IRQ_NONE;
                else if (ret < 0) {
                        dev_err(&pdev->dev, "failed to get IRQ\n");
-                       goto bad1;
+                       return ret;
                }
        }
 
@@ -209,20 +208,12 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
        ret = uio_register_device(&pdev->dev, priv->uioinfo);
        if (ret) {
                dev_err(&pdev->dev, "unable to register uio device\n");
-               goto bad2;
+               pm_runtime_disable(&pdev->dev);
+               return ret;
        }
 
        platform_set_drvdata(pdev, priv);
        return 0;
- bad2:
-       pm_runtime_disable(&pdev->dev);
- bad1:
-       kfree(priv);
- bad0:
-       /* kfree uioinfo for OF */
-       if (pdev->dev.of_node)
-               kfree(uioinfo);
-       return ret;
 }
 
 static int uio_pdrv_genirq_remove(struct platform_device *pdev)
@@ -235,11 +226,6 @@ static int uio_pdrv_genirq_remove(struct platform_device *pdev)
        priv->uioinfo->handler = NULL;
        priv->uioinfo->irqcontrol = NULL;
 
-       /* kfree uioinfo for OF */
-       if (pdev->dev.of_node)
-               kfree(priv->uioinfo);
-
-       kfree(priv);
        return 0;
 }
 
index 5419832170856b78b384659f8bc961c3a283f2ee..9cfdfcafa2621de3feab90f003a68854a8b36eca 100644 (file)
@@ -188,7 +188,6 @@ static void sercos3_pci_remove(struct pci_dev *dev)
        uio_unregister_device(info);
        pci_release_regions(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
        for (i = 0; i < 5; i++) {
                if (info->mem[i].internal_addr)
                        iounmap(info->mem[i].internal_addr);
index 5651231a74371cc4b97a0b55769e078478158a35..f3eecd967a8a1cb152575828cf45741dfc2bf85d 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/stringify.h>
 #include <linux/usb.h>
 #include <linux/mutex.h>
+#include <linux/ratelimit.h>
 
 /*
 #define VERBOSE_DEBUG
        atm_printk(KERN_INFO, instance , format , ## arg)
 #define atm_warn(instance, format, arg...)     \
        atm_printk(KERN_WARNING, instance , format , ## arg)
-#define atm_dbg(instance, format, arg...)              \
-       dynamic_pr_debug("ATM dev %d: " format ,        \
-       (instance)->atm_dev->number , ## arg)
-#define atm_rldbg(instance, format, arg...)            \
-       if (printk_ratelimit())                         \
-               atm_dbg(instance , format , ## arg)
-
+#define atm_dbg(instance, format, ...)                                 \
+       pr_debug("ATM dev %d: " format,                                 \
+                (instance)->atm_dev->number, ##__VA_ARGS__)
+#define atm_rldbg(instance, format, ...)                               \
+       pr_debug_ratelimited("ATM dev %d: " format,                     \
+                            (instance)->atm_dev->number, ##__VA_ARGS__)
 
 /* flags, set by mini-driver in bind() */
 
index 464584c6ccaeaf84d87a888d2e4b8c1382f97e61..a857131656889fa2a2634dc33ed398a5143c373a 100644 (file)
@@ -48,6 +48,7 @@
 #define PORTSC_SUSP           BIT(7)
 #define PORTSC_HSP            BIT(9)
 #define PORTSC_PTC            (0x0FUL << 16)
+#define PORTSC_PHCD(d)       ((d) ? BIT(22) : BIT(23))
 /* PTS and PTW for non lpm version only */
 #define PORTSC_PTS(d)                                          \
        (u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
index be822a2c1776cc30c2df5f3ed7840aff68ef10ac..023d3cb6aa0a1bb04e69403f3e4bd825e4ada8d9 100644 (file)
@@ -108,14 +108,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
        }
 
        data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
-       if (!IS_ERR(data->phy)) {
-               ret = usb_phy_init(data->phy);
-               if (ret) {
-                       dev_err(&pdev->dev, "unable to init phy: %d\n", ret);
-                       goto err_clk;
-               }
-       } else if (PTR_ERR(data->phy) == -EPROBE_DEFER) {
-               ret = -EPROBE_DEFER;
+       if (IS_ERR(data->phy)) {
+               ret = PTR_ERR(data->phy);
                goto err_clk;
        }
 
@@ -131,7 +125,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
                if (ret) {
                        dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n",
                                        ret);
-                       goto err_phy;
+                       goto err_clk;
                }
        }
 
@@ -143,7 +137,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
                dev_err(&pdev->dev,
                        "Can't register ci_hdrc platform device, err=%d\n",
                        ret);
-               goto err_phy;
+               goto err_clk;
        }
 
        if (data->usbmisc_data) {
@@ -164,9 +158,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
 
 disable_device:
        ci_hdrc_remove_device(data->ci_pdev);
-err_phy:
-       if (data->phy)
-               usb_phy_shutdown(data->phy);
 err_clk:
        clk_disable_unprepare(data->clk);
        return ret;
@@ -178,10 +169,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
 
        pm_runtime_disable(&pdev->dev);
        ci_hdrc_remove_device(data->ci_pdev);
-
-       if (data->phy)
-               usb_phy_shutdown(data->phy);
-
        clk_disable_unprepare(data->clk);
 
        return 0;
index 23763dcec069b2e8d1ba6dd140fc9cf644d2ddec..5d8981c5235e50e42776cfb9f672977c11a61ef0 100644 (file)
@@ -172,6 +172,27 @@ u8 hw_port_test_get(struct ci_hdrc *ci)
        return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
 }
 
+/* The PHY enters/leaves low power mode */
+static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
+{
+       enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
+       bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
+
+       if (enable && !lpm) {
+               hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
+                               PORTSC_PHCD(ci->hw_bank.lpm));
+       } else  if (!enable && lpm) {
+               hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
+                               0);
+               /* 
+                * The controller needs at least 1ms to reflect
+                * PHY's status, the PHY also needs some time (less
+                * than 1ms) to leave low power mode.
+                */
+               usleep_range(1500, 2000);
+       }
+}
+
 static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
 {
        u32 reg;
@@ -199,6 +220,8 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
        if (ci->hw_ep_max > ENDPT_MAX)
                return -ENODEV;
 
+       ci_hdrc_enter_lpm(ci, false);
+
        /* Disable all interrupts bits */
        hw_write(ci, OP_USBINTR, 0xffffffff, 0);
 
@@ -369,16 +392,28 @@ static irqreturn_t ci_irq(int irq, void *data)
 static int ci_get_platdata(struct device *dev,
                struct ci_hdrc_platform_data *platdata)
 {
-       /* Get the vbus regulator */
-       platdata->reg_vbus = devm_regulator_get(dev, "vbus");
-       if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
-               return -EPROBE_DEFER;
-       } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
-               platdata->reg_vbus = NULL; /* no vbus regualator is needed */
-       } else if (IS_ERR(platdata->reg_vbus)) {
-               dev_err(dev, "Getting regulator error: %ld\n",
-                       PTR_ERR(platdata->reg_vbus));
-               return PTR_ERR(platdata->reg_vbus);
+       if (!platdata->phy_mode)
+               platdata->phy_mode = of_usb_get_phy_mode(dev->of_node);
+
+       if (!platdata->dr_mode)
+               platdata->dr_mode = of_usb_get_dr_mode(dev->of_node);
+
+       if (platdata->dr_mode == USB_DR_MODE_UNKNOWN)
+               platdata->dr_mode = USB_DR_MODE_OTG;
+
+       if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) {
+               /* Get the vbus regulator */
+               platdata->reg_vbus = devm_regulator_get(dev, "vbus");
+               if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
+                       return -EPROBE_DEFER;
+               } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
+                       /* no vbus regualator is needed */
+                       platdata->reg_vbus = NULL;
+               } else if (IS_ERR(platdata->reg_vbus)) {
+                       dev_err(dev, "Getting regulator error: %ld\n",
+                               PTR_ERR(platdata->reg_vbus));
+                       return PTR_ERR(platdata->reg_vbus);
+               }
        }
 
        return 0;
@@ -465,6 +500,33 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
        }
 }
 
+static int ci_usb_phy_init(struct ci_hdrc *ci)
+{
+       if (ci->platdata->phy) {
+               ci->transceiver = ci->platdata->phy;
+               return usb_phy_init(ci->transceiver);
+       } else {
+               ci->global_phy = true;
+               ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+               if (IS_ERR(ci->transceiver))
+                       ci->transceiver = NULL;
+
+               return 0;
+       }
+}
+
+static void ci_usb_phy_destroy(struct ci_hdrc *ci)
+{
+       if (!ci->transceiver)
+               return;
+
+       otg_set_peripheral(ci->transceiver->otg, NULL);
+       if (ci->global_phy)
+               usb_put_phy(ci->transceiver);
+       else
+               usb_phy_shutdown(ci->transceiver);
+}
+
 static int ci_hdrc_probe(struct platform_device *pdev)
 {
        struct device   *dev = &pdev->dev;
@@ -473,7 +535,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        void __iomem    *base;
        int             ret;
        enum usb_dr_mode dr_mode;
-       struct device_node *of_node = dev->of_node ?: dev->parent->of_node;
 
        if (!dev->platform_data) {
                dev_err(dev, "platform data missing\n");
@@ -493,10 +554,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 
        ci->dev = dev;
        ci->platdata = dev->platform_data;
-       if (ci->platdata->phy)
-               ci->transceiver = ci->platdata->phy;
-       else
-               ci->global_phy = true;
 
        ret = hw_device_init(ci, base);
        if (ret < 0) {
@@ -504,27 +561,25 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
+       ret = ci_usb_phy_init(ci);
+       if (ret) {
+               dev_err(dev, "unable to init phy: %d\n", ret);
+               return ret;
+       }
+
        ci->hw_bank.phys = res->start;
 
        ci->irq = platform_get_irq(pdev, 0);
        if (ci->irq < 0) {
                dev_err(dev, "missing IRQ\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto destroy_phy;
        }
 
        ci_get_otg_capable(ci);
 
-       if (!ci->platdata->phy_mode)
-               ci->platdata->phy_mode = of_usb_get_phy_mode(of_node);
-
        hw_phymode_configure(ci);
 
-       if (!ci->platdata->dr_mode)
-               ci->platdata->dr_mode = of_usb_get_dr_mode(of_node);
-
-       if (ci->platdata->dr_mode == USB_DR_MODE_UNKNOWN)
-               ci->platdata->dr_mode = USB_DR_MODE_OTG;
-
        dr_mode = ci->platdata->dr_mode;
        /* initialize role(s) before the interrupt is requested */
        if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
@@ -537,11 +592,23 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                ret = ci_hdrc_gadget_init(ci);
                if (ret)
                        dev_info(dev, "doesn't support gadget\n");
+               if (!ret && ci->transceiver) {
+                       ret = otg_set_peripheral(ci->transceiver->otg,
+                                                       &ci->gadget);
+                       /*
+                        * If we implement all USB functions using chipidea drivers,
+                        * it doesn't need to call above API, meanwhile, if we only
+                        * use gadget function, calling above API is useless.
+                        */
+                       if (ret && ret != -ENOTSUPP)
+                               goto destroy_phy;
+               }
        }
 
        if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
                dev_err(dev, "no supported roles\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto destroy_phy;
        }
 
        if (ci->is_otg) {
@@ -594,6 +661,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        free_irq(ci->irq, ci);
 stop:
        ci_role_destroy(ci);
+destroy_phy:
+       ci_usb_phy_destroy(ci);
 
        return ret;
 }
@@ -605,6 +674,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
        dbg_remove_files(ci);
        free_irq(ci->irq, ci);
        ci_role_destroy(ci);
+       ci_hdrc_enter_lpm(ci, true);
+       ci_usb_phy_destroy(ci);
        kfree(ci->hw_bank.regmap);
 
        return 0;
index 64d7a6d9a1adcbd679258661a6b5875bd8bf834a..59e6020ea7539e5e331364ac067c9a16f27855ba 100644 (file)
@@ -103,15 +103,15 @@ static void host_stop(struct ci_hdrc *ci)
        if (hcd) {
                usb_remove_hcd(hcd);
                usb_put_hcd(hcd);
+               if (ci->platdata->reg_vbus)
+                       regulator_disable(ci->platdata->reg_vbus);
        }
-       if (ci->platdata->reg_vbus)
-               regulator_disable(ci->platdata->reg_vbus);
 }
 
 
 void ci_hdrc_host_destroy(struct ci_hdrc *ci)
 {
-       if (ci->role == CI_ROLE_HOST)
+       if (ci->role == CI_ROLE_HOST && ci->hcd)
                host_stop(ci);
 }
 
index 9333083dd1111c047c7016a44402242e2aa87f7d..b34c81969cba672a7e880f405f2445adf36b7e6f 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
-#include <linux/usb/otg.h>
 #include <linux/usb/chipidea.h>
 
 #include "ci.h"
@@ -686,9 +685,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
        usb_ep_fifo_flush(&ci->ep0out->ep);
        usb_ep_fifo_flush(&ci->ep0in->ep);
 
-       if (ci->driver)
-               ci->driver->disconnect(gadget);
-
        /* make sure to disable all endpoints */
        gadget_for_each_ep(ep, gadget) {
                usb_ep_disable(ep);
@@ -718,6 +714,11 @@ __acquires(ci->lock)
        int retval;
 
        spin_unlock(&ci->lock);
+       if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
+               if (ci->driver)
+                       ci->driver->disconnect(&ci->gadget);
+       }
+
        retval = _gadget_stop_activity(&ci->gadget);
        if (retval)
                goto done;
@@ -1461,6 +1462,8 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
                        hw_device_state(ci, ci->ep0out->qh.dma);
                        dev_dbg(ci->dev, "Connected to host\n");
                } else {
+                       if (ci->driver)
+                               ci->driver->disconnect(&ci->gadget);
                        hw_device_state(ci, 0);
                        if (ci->platdata->notify_event)
                                ci->platdata->notify_event(ci,
@@ -1633,23 +1636,22 @@ static int ci_udc_start(struct usb_gadget *gadget,
        retval = usb_ep_enable(&ci->ep0in->ep);
        if (retval)
                return retval;
-       spin_lock_irqsave(&ci->lock, flags);
 
        ci->driver = driver;
        pm_runtime_get_sync(&ci->gadget.dev);
        if (ci->vbus_active) {
+               spin_lock_irqsave(&ci->lock, flags);
                hw_device_reset(ci, USBMODE_CM_DC);
        } else {
                pm_runtime_put_sync(&ci->gadget.dev);
-               goto done;
+               return retval;
        }
 
        retval = hw_device_state(ci, ci->ep0out->qh.dma);
+       spin_unlock_irqrestore(&ci->lock, flags);
        if (retval)
                pm_runtime_put_sync(&ci->gadget.dev);
 
- done:
-       spin_unlock_irqrestore(&ci->lock, flags);
        return retval;
 }
 
@@ -1786,34 +1788,9 @@ static int udc_start(struct ci_hdrc *ci)
 
        ci->gadget.ep0 = &ci->ep0in->ep;
 
-       if (ci->global_phy) {
-               ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
-               if (IS_ERR(ci->transceiver))
-                       ci->transceiver = NULL;
-       }
-
-       if (ci->platdata->flags & CI_HDRC_REQUIRE_TRANSCEIVER) {
-               if (ci->transceiver == NULL) {
-                       retval = -ENODEV;
-                       goto destroy_eps;
-               }
-       }
-
-       if (ci->transceiver) {
-               retval = otg_set_peripheral(ci->transceiver->otg,
-                                               &ci->gadget);
-               /*
-                * If we implement all USB functions using chipidea drivers,
-                * it doesn't need to call above API, meanwhile, if we only
-                * use gadget function, calling above API is useless.
-                */
-               if (retval && retval != -ENOTSUPP)
-                       goto put_transceiver;
-       }
-
        retval = usb_add_gadget_udc(dev, &ci->gadget);
        if (retval)
-               goto remove_trans;
+               goto destroy_eps;
 
        pm_runtime_no_callbacks(&ci->gadget.dev);
        pm_runtime_enable(&ci->gadget.dev);
@@ -1823,17 +1800,6 @@ static int udc_start(struct ci_hdrc *ci)
 
        return retval;
 
-remove_trans:
-       if (ci->transceiver) {
-               otg_set_peripheral(ci->transceiver->otg, NULL);
-               if (ci->global_phy)
-                       usb_put_phy(ci->transceiver);
-       }
-
-       dev_err(dev, "error = %i\n", retval);
-put_transceiver:
-       if (ci->transceiver && ci->global_phy)
-               usb_put_phy(ci->transceiver);
 destroy_eps:
        destroy_eps(ci);
 free_pools:
index d3318a0df8ee503f4f5ee553cda3ff1391298be4..4d387596f3f0a6e531caa1a9049a383d605091b4 100644 (file)
@@ -101,6 +101,7 @@ struct wdm_device {
        struct work_struct      rxwork;
        int                     werr;
        int                     rerr;
+       int                     resp_count;
 
        struct list_head        device_list;
        int                     (*manage_power)(struct usb_interface *, int);
@@ -253,6 +254,10 @@ static void wdm_int_callback(struct urb *urb)
                        "NOTIFY_NETWORK_CONNECTION %s network",
                        dr->wValue ? "connected to" : "disconnected from");
                goto exit;
+       case USB_CDC_NOTIFY_SPEED_CHANGE:
+               dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)",
+                       urb->actual_length);
+               goto exit;
        default:
                clear_bit(WDM_POLL_RUNNING, &desc->flags);
                dev_err(&desc->intf->dev,
@@ -262,9 +267,9 @@ static void wdm_int_callback(struct urb *urb)
        }
 
        spin_lock(&desc->iuspin);
-       clear_bit(WDM_READ, &desc->flags);
        responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
-       if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags)
+       if (!desc->resp_count++ && !responding
+               && !test_bit(WDM_DISCONNECTING, &desc->flags)
                && !test_bit(WDM_SUSPENDING, &desc->flags)) {
                rv = usb_submit_urb(desc->response, GFP_ATOMIC);
                dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
@@ -521,10 +526,36 @@ retry:
 
        desc->length -= cntr;
        /* in case we had outstanding data */
-       if (!desc->length)
+       if (!desc->length) {
                clear_bit(WDM_READ, &desc->flags);
 
-       spin_unlock_irq(&desc->iuspin);
+               if (--desc->resp_count) {
+                       set_bit(WDM_RESPONDING, &desc->flags);
+                       spin_unlock_irq(&desc->iuspin);
+
+                       rv = usb_submit_urb(desc->response, GFP_KERNEL);
+                       if (rv) {
+                               dev_err(&desc->intf->dev,
+                                       "%s: usb_submit_urb failed with result %d\n",
+                                       __func__, rv);
+                               spin_lock_irq(&desc->iuspin);
+                               clear_bit(WDM_RESPONDING, &desc->flags);
+                               spin_unlock_irq(&desc->iuspin);
+
+                               if (rv == -ENOMEM) {
+                                       rv = schedule_work(&desc->rxwork);
+                                       if (rv)
+                                               dev_err(&desc->intf->dev, "Cannot schedule work\n");
+                               } else {
+                                       spin_lock_irq(&desc->iuspin);
+                                       desc->resp_count = 0;
+                                       spin_unlock_irq(&desc->iuspin);
+                               }
+                       }
+               } else
+                       spin_unlock_irq(&desc->iuspin);
+       } else
+               spin_unlock_irq(&desc->iuspin);
 
        rv = cntr;
 
@@ -635,6 +666,9 @@ static int wdm_release(struct inode *inode, struct file *file)
                if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
                        dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
                        kill_urbs(desc);
+                       spin_lock_irq(&desc->iuspin);
+                       desc->resp_count = 0;
+                       spin_unlock_irq(&desc->iuspin);
                        desc->manage_power(desc->intf, 0);
                } else {
                        /* must avoid dev_printk here as desc->intf is invalid */
index 71dc5d768fa5cef3edc5ac27c84c032d2a0a0dcf..967152a63bd3c92f86f937a5b2f3c7b1d0805cc2 100644 (file)
@@ -914,10 +914,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)
        snoop(&dev->dev, "control urb: bRequestType=%02x "
                "bRequest=%02x wValue=%04x "
                "wIndex=%04x wLength=%04x\n",
-               ctrl.bRequestType, ctrl.bRequest,
-               __le16_to_cpup(&ctrl.wValue),
-               __le16_to_cpup(&ctrl.wIndex),
-               __le16_to_cpup(&ctrl.wLength));
+               ctrl.bRequestType, ctrl.bRequest, ctrl.wValue,
+               ctrl.wIndex, ctrl.wLength);
        if (ctrl.bRequestType & 0x80) {
                if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,
                                               ctrl.wLength)) {
@@ -1636,32 +1634,32 @@ static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
 static int proc_control_compat(struct dev_state *ps,
                                struct usbdevfs_ctrltransfer32 __user *p32)
 {
-        struct usbdevfs_ctrltransfer __user *p;
-        __u32 udata;
-        p = compat_alloc_user_space(sizeof(*p));
-        if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
-            get_user(udata, &p32->data) ||
+       struct usbdevfs_ctrltransfer __user *p;
+       __u32 udata;
+       p = compat_alloc_user_space(sizeof(*p));
+       if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
+           get_user(udata, &p32->data) ||
            put_user(compat_ptr(udata), &p->data))
                return -EFAULT;
-        return proc_control(ps, p);
+       return proc_control(ps, p);
 }
 
 static int proc_bulk_compat(struct dev_state *ps,
                        struct usbdevfs_bulktransfer32 __user *p32)
 {
-        struct usbdevfs_bulktransfer __user *p;
-        compat_uint_t n;
-        compat_caddr_t addr;
+       struct usbdevfs_bulktransfer __user *p;
+       compat_uint_t n;
+       compat_caddr_t addr;
 
-        p = compat_alloc_user_space(sizeof(*p));
+       p = compat_alloc_user_space(sizeof(*p));
 
-        if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
-            get_user(n, &p32->len) || put_user(n, &p->len) ||
-            get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
-            get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
-                return -EFAULT;
+       if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
+           get_user(n, &p32->len) || put_user(n, &p->len) ||
+           get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
+           get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
+               return -EFAULT;
 
-        return proc_bulk(ps, p);
+       return proc_bulk(ps, p);
 }
 static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg)
 {
index f7841d44feda967f0a78b21bf4e8229556ccd7c5..47aade2a5e741ade190022342590c363316514d7 100644 (file)
@@ -1179,8 +1179,8 @@ static int usb_resume_interface(struct usb_device *udev,
                                                "reset_resume", status);
                } else {
                        intf->needs_binding = 1;
-                       dev_warn(&intf->dev, "no %s for driver %s?\n",
-                                       "reset_resume", driver->name);
+                       dev_dbg(&intf->dev, "no reset_resume for driver %s?\n",
+                                       driver->name);
                }
        } else {
                status = driver->resume(intf);
@@ -1790,6 +1790,9 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
        struct usb_hcd *hcd = bus_to_hcd(udev->bus);
        int ret = -EPERM;
 
+       if (enable && !udev->usb2_hw_lpm_allowed)
+               return 0;
+
        if (hcd->driver->set_usb2_hw_lpm) {
                ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
                if (!ret)
index 7421888087a3b752a3915c0dc70bfcda640d9122..3bdfbf88a0aee73e36989c8d7adfbe3190b2e8ce 100644 (file)
@@ -8,7 +8,7 @@
  * (C) Copyright Deti Fliegl 1999 (new USB architecture)
  * (C) Copyright Randy Dunlap 2000
  * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
      more docs, etc)
*     more docs, etc)
  * (C) Copyright Yggdrasil Computing, Inc. 2000
  *     (usb_device_id matching changes by Adam J. Richter)
  * (C) Copyright Greg Kroah-Hartman 2002-2003
@@ -27,7 +27,7 @@
 static const struct file_operations *usb_minors[MAX_USB_MINORS];
 static DECLARE_RWSEM(minor_rwsem);
 
-static int usb_open(struct inode * inode, struct file * file)
+static int usb_open(struct inode *inode, struct file *file)
 {
        int minor = iminor(inode);
        const struct file_operations *c;
@@ -44,7 +44,7 @@ static int usb_open(struct inode * inode, struct file * file)
        file->f_op = new_fops;
        /* Curiouser and curiouser... NULL ->open() as "no device" ? */
        if (file->f_op->open)
-               err = file->f_op->open(inode,file);
+               err = file->f_op->open(inode, file);
        if (err) {
                fops_put(file->f_op);
                file->f_op = fops_get(old_fops);
@@ -166,7 +166,7 @@ int usb_register_dev(struct usb_interface *intf,
        char *temp;
 
 #ifdef CONFIG_USB_DYNAMIC_MINORS
-       /* 
+       /*
         * We don't care what the device tries to start at, we want to start
         * at zero to pack the devices into the smallest available space with
         * no holes in the minor range.
index b9d3c43e38599e67c4cc8c409c686bb4f45fcf8a..dfe9d0f229780653c2bcb505e73ee00f4b372197 100644 (file)
@@ -215,6 +215,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
                goto disable_pci;
        }
 
+       hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
+                       driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
+
        if (driver->flags & HCD_MEMORY) {
                /* EHCI, OHCI */
                hcd->rsrc_start = pci_resource_start(dev, 0);
index d6a8d23f047ba69c33d836adb8adc9e0741a85c5..6bffb8c87bc9aaee98c2342b0f7a5a3c05c870b0 100644 (file)
@@ -6,7 +6,7 @@
  * (C) Copyright Deti Fliegl 1999
  * (C) Copyright Randy Dunlap 2000
  * (C) Copyright David Brownell 2000-2002
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -40,6 +40,7 @@
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
 #include <linux/pm_runtime.h>
+#include <linux/types.h>
 
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
@@ -92,10 +93,7 @@ EXPORT_SYMBOL_GPL (usb_bus_list);
 
 /* used when allocating bus numbers */
 #define USB_MAXBUS             64
-struct usb_busmap {
-       unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
-};
-static struct usb_busmap busmap;
+static DECLARE_BITMAP(busmap, USB_MAXBUS);
 
 /* used when updating list of hcds */
 DEFINE_MUTEX(usb_bus_list_lock);       /* exported only for usbfs */
@@ -171,7 +169,7 @@ static const u8 usb25_rh_dev_descriptor[18] = {
 };
 
 /* usb 2.0 root hub device descriptor */
-static const u8 usb2_rh_dev_descriptor [18] = {
+static const u8 usb2_rh_dev_descriptor[18] = {
        0x12,       /*  __u8  bLength; */
        0x01,       /*  __u8  bDescriptorType; Device */
        0x00, 0x02, /*  __le16 bcdUSB; v2.0 */
@@ -194,7 +192,7 @@ static const u8 usb2_rh_dev_descriptor [18] = {
 /* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
 
 /* usb 1.1 root hub device descriptor */
-static const u8 usb11_rh_dev_descriptor [18] = {
+static const u8 usb11_rh_dev_descriptor[18] = {
        0x12,       /*  __u8  bLength; */
        0x01,       /*  __u8  bDescriptorType; Device */
        0x10, 0x01, /*  __le16 bcdUSB; v1.1 */
@@ -219,7 +217,7 @@ static const u8 usb11_rh_dev_descriptor [18] = {
 
 /* Configuration descriptors for our root hubs */
 
-static const u8 fs_rh_config_descriptor [] = {
+static const u8 fs_rh_config_descriptor[] = {
 
        /* one configuration */
        0x09,       /*  __u8  bLength; */
@@ -228,13 +226,13 @@ static const u8 fs_rh_config_descriptor [] = {
        0x01,       /*  __u8  bNumInterfaces; (1) */
        0x01,       /*  __u8  bConfigurationValue; */
        0x00,       /*  __u8  iConfiguration; */
-       0xc0,       /*  __u8  bmAttributes; 
+       0xc0,       /*  __u8  bmAttributes;
                                 Bit 7: must be set,
                                     6: Self-powered,
                                     5: Remote wakeup,
                                     4..0: resvd */
        0x00,       /*  __u8  MaxPower; */
-      
+
        /* USB 1.1:
         * USB 2.0, single TT organization (mandatory):
         *      one interface, protocol 0
@@ -256,17 +254,17 @@ static const u8 fs_rh_config_descriptor [] = {
        0x00,       /*  __u8  if_bInterfaceSubClass; */
        0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
        0x00,       /*  __u8  if_iInterface; */
-     
+
        /* one endpoint (status change endpoint) */
        0x07,       /*  __u8  ep_bLength; */
        0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
        0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
-       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
-       0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+       0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
        0xff        /*  __u8  ep_bInterval; (255ms -- usb 2.0 spec) */
 };
 
-static const u8 hs_rh_config_descriptor [] = {
+static const u8 hs_rh_config_descriptor[] = {
 
        /* one configuration */
        0x09,       /*  __u8  bLength; */
@@ -275,13 +273,13 @@ static const u8 hs_rh_config_descriptor [] = {
        0x01,       /*  __u8  bNumInterfaces; (1) */
        0x01,       /*  __u8  bConfigurationValue; */
        0x00,       /*  __u8  iConfiguration; */
-       0xc0,       /*  __u8  bmAttributes; 
+       0xc0,       /*  __u8  bmAttributes;
                                 Bit 7: must be set,
                                     6: Self-powered,
                                     5: Remote wakeup,
                                     4..0: resvd */
        0x00,       /*  __u8  MaxPower; */
-      
+
        /* USB 1.1:
         * USB 2.0, single TT organization (mandatory):
         *      one interface, protocol 0
@@ -303,12 +301,12 @@ static const u8 hs_rh_config_descriptor [] = {
        0x00,       /*  __u8  if_bInterfaceSubClass; */
        0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
        0x00,       /*  __u8  if_iInterface; */
-     
+
        /* one endpoint (status change endpoint) */
        0x07,       /*  __u8  ep_bLength; */
        0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
        0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
-       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
                    /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
                     * see hub.c:hub_configure() for details. */
        (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
@@ -428,7 +426,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
        char const *s;
        static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04};
 
-       // language ids
+       /* language ids */
        switch (id) {
        case 0:
                /* Array of LANGID codes (0x0409 is MSFT-speak for "en-us") */
@@ -464,7 +462,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
 static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 {
        struct usb_ctrlrequest *cmd;
-       u16             typeReq, wValue, wIndex, wLength;
+       u16             typeReq, wValue, wIndex, wLength;
        u8              *ubuf = urb->transfer_buffer;
        unsigned        len = 0;
        int             status;
@@ -526,10 +524,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
         */
 
        case DeviceRequest | USB_REQ_GET_STATUS:
-               tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
+               tbuf[0] = (device_may_wakeup(&hcd->self.root_hub->dev)
                                        << USB_DEVICE_REMOTE_WAKEUP)
                                | (1 << USB_DEVICE_SELF_POWERED);
-               tbuf [1] = 0;
+               tbuf[1] = 0;
                len = 2;
                break;
        case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -546,7 +544,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
                        goto error;
                break;
        case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-               tbuf [0] = 1;
+               tbuf[0] = 1;
                len = 1;
                        /* FALLTHROUGH */
        case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
@@ -609,13 +607,13 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
                }
                break;
        case DeviceRequest | USB_REQ_GET_INTERFACE:
-               tbuf [0] = 0;
+               tbuf[0] = 0;
                len = 1;
                        /* FALLTHROUGH */
        case DeviceOutRequest | USB_REQ_SET_INTERFACE:
                break;
        case DeviceOutRequest | USB_REQ_SET_ADDRESS:
-               // wValue == urb->dev->devaddr
+               /* wValue == urb->dev->devaddr */
                dev_dbg (hcd->self.controller, "root hub device address %d\n",
                        wValue);
                break;
@@ -625,9 +623,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
        /* ENDPOINT REQUESTS */
 
        case EndpointRequest | USB_REQ_GET_STATUS:
-               // ENDPOINT_HALT flag
-               tbuf [0] = 0;
-               tbuf [1] = 0;
+               /* ENDPOINT_HALT flag */
+               tbuf[0] = 0;
+               tbuf[1] = 0;
                len = 2;
                        /* FALLTHROUGH */
        case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -683,7 +681,7 @@ error:
                if (urb->transfer_buffer_length < len)
                        len = urb->transfer_buffer_length;
                urb->actual_length = len;
-               // always USB_DIR_IN, toward host
+               /* always USB_DIR_IN, toward host */
                memcpy (ubuf, bufp, len);
 
                /* report whether RH hardware supports remote wakeup */
@@ -877,11 +875,11 @@ static ssize_t authorized_default_store(struct device *dev,
        usb_hcd = bus_to_hcd(usb_bus);
        result = sscanf(buf, "%u\n", &val);
        if (result == 1) {
-               usb_hcd->authorized_default = val? 1 : 0;
+               usb_hcd->authorized_default = val ? 1 : 0;
                result = size;
-       }
-       else
+       } else {
                result = -EINVAL;
+       }
        return result;
 }
 static DEVICE_ATTR_RW(authorized_default);
@@ -941,12 +939,12 @@ static int usb_register_bus(struct usb_bus *bus)
        int busnum;
 
        mutex_lock(&usb_bus_list_lock);
-       busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
+       busnum = find_next_zero_bit(busmap, USB_MAXBUS, 1);
        if (busnum >= USB_MAXBUS) {
                printk (KERN_ERR "%s: too many buses\n", usbcore_name);
                goto error_find_busnum;
        }
-       set_bit (busnum, busmap.busmap);
+       set_bit(busnum, busmap);
        bus->busnum = busnum;
 
        /* Add it to the local list of buses */
@@ -987,7 +985,7 @@ static void usb_deregister_bus (struct usb_bus *bus)
 
        usb_notify_remove_bus(bus);
 
-       clear_bit (bus->busnum, busmap.busmap);
+       clear_bit(bus->busnum, busmap);
 }
 
 /**
@@ -1033,6 +1031,7 @@ static int register_root_hub(struct usb_hcd *hcd)
                                        dev_name(&usb_dev->dev), retval);
                        return retval;
                }
+               usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
        }
 
        retval = usb_new_device (usb_dev);
@@ -1120,21 +1119,21 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
        case USB_SPEED_LOW:     /* INTR only */
                if (is_input) {
                        tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
-                       return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+                       return 64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
                } else {
                        tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
-                       return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+                       return 64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
                }
        case USB_SPEED_FULL:    /* ISOC or INTR */
                if (isoc) {
                        tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
-                       return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+                       return ((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp;
                } else {
                        tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
-                       return (9107L + BW_HOST_DELAY + tmp);
+                       return 9107L + BW_HOST_DELAY + tmp;
                }
        case USB_SPEED_HIGH:    /* ISOC or INTR */
-               // FIXME adjust for input vs output
+               /* FIXME adjust for input vs output */
                if (isoc)
                        tmp = HS_NSECS_ISO (bytecount);
                else
@@ -1651,6 +1650,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
 static void __usb_hcd_giveback_urb(struct urb *urb)
 {
        struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
+       struct usb_anchor *anchor = urb->anchor;
        int status = urb->unlinked;
        unsigned long flags;
 
@@ -1662,6 +1662,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
 
        unmap_urb_for_dma(hcd, urb);
        usbmon_urb_complete(&hcd->self, urb, status);
+       usb_anchor_suspend_wakeups(anchor);
        usb_unanchor_urb(urb);
 
        /* pass ownership to the completion handler */
@@ -1681,6 +1682,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
        urb->complete(urb);
        local_irq_restore(flags);
 
+       usb_anchor_resume_wakeups(anchor);
        atomic_dec(&urb->use_count);
        if (unlikely(atomic_read(&urb->reject)))
                wake_up(&usb_kill_urb_queue);
@@ -1703,7 +1705,9 @@ static void usb_giveback_urb_bh(unsigned long param)
 
                urb = list_entry(local_list.next, struct urb, urb_list);
                list_del_init(&urb->urb_list);
+               bh->completing_ep = urb->ep;
                __usb_hcd_giveback_urb(urb);
+               bh->completing_ep = NULL;
        }
 
        /* check if there are new URBs to giveback */
@@ -1812,7 +1816,7 @@ rescan:
                                 case USB_ENDPOINT_XFER_INT:
                                        s = "-intr"; break;
                                 default:
-                                       s = "-iso"; break;
+                                       s = "-iso"; break;
                                };
                                s;
                        }));
@@ -2073,8 +2077,11 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams);
  *
  * Reverts a group of bulk endpoints back to not using stream IDs.
  * Can fail if we are given bad arguments, or HCD is broken.
+ *
+ * Return: On success, the number of allocated streams. On failure, a negative
+ * error code.
  */
-void usb_free_streams(struct usb_interface *interface,
+int usb_free_streams(struct usb_interface *interface,
                struct usb_host_endpoint **eps, unsigned int num_eps,
                gfp_t mem_flags)
 {
@@ -2085,14 +2092,14 @@ void usb_free_streams(struct usb_interface *interface,
        dev = interface_to_usbdev(interface);
        hcd = bus_to_hcd(dev->bus);
        if (dev->speed != USB_SPEED_SUPER)
-               return;
+               return -EINVAL;
 
        /* Streams only apply to bulk endpoints. */
        for (i = 0; i < num_eps; i++)
                if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
-                       return;
+                       return -EINVAL;
 
-       hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
+       return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
 }
 EXPORT_SYMBOL_GPL(usb_free_streams);
 
@@ -2245,7 +2252,7 @@ static void hcd_resume_work(struct work_struct *work)
 }
 
 /**
- * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
+ * usb_hcd_resume_root_hub - called by HCD to resume its root hub
  * @hcd: host controller for this root hub
  *
  * The USB host controller calls this function when its root hub is
@@ -2324,15 +2331,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
 irqreturn_t usb_hcd_irq (int irq, void *__hcd)
 {
        struct usb_hcd          *hcd = __hcd;
-       unsigned long           flags;
        irqreturn_t             rc;
 
-       /* IRQF_DISABLED doesn't work correctly with shared IRQs
-        * when the first handler doesn't use it.  So let's just
-        * assume it's never used.
-        */
-       local_irq_save(flags);
-
        if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
                rc = IRQ_NONE;
        else if (hcd->driver->irq(hcd) == IRQ_NONE)
@@ -2340,7 +2340,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
        else
                rc = IRQ_HANDLED;
 
-       local_irq_restore(flags);
        return rc;
 }
 EXPORT_SYMBOL_GPL(usb_hcd_irq);
@@ -2547,13 +2546,6 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
 
        if (hcd->driver->irq) {
 
-               /* IRQF_DISABLED doesn't work as advertised when used together
-                * with IRQF_SHARED. As usb_hcd_irq() will always disable
-                * interrupts we can remove it here.
-                */
-               if (irqflags & IRQF_SHARED)
-                       irqflags &= ~IRQF_DISABLED;
-
                snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
                                hcd->driver->description, hcd->self.busnum);
                retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
@@ -2600,7 +2592,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
 
        /* Keep old behaviour if authorized_default is not in [0, 1]. */
        if (authorized_default < 0 || authorized_default > 1)
-               hcd->authorized_default = hcd->wireless? 0 : 1;
+               hcd->authorized_default = hcd->wireless ? 0 : 1;
        else
                hcd->authorized_default = authorized_default;
        set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
@@ -2743,7 +2735,7 @@ err_allocate_root_hub:
 err_register_bus:
        hcd_buffer_destroy(hcd);
        return retval;
-} 
+}
 EXPORT_SYMBOL_GPL(usb_add_hcd);
 
 /**
@@ -2818,7 +2810,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 EXPORT_SYMBOL_GPL(usb_remove_hcd);
 
 void
-usb_hcd_platform_shutdown(struct platform_devicedev)
+usb_hcd_platform_shutdown(struct platform_device *dev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(dev);
 
@@ -2840,7 +2832,7 @@ struct usb_mon_operations *mon_ops;
  * Notice that the code is minimally error-proof. Because usbmon needs
  * symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
  */
+
 int usb_mon_register (struct usb_mon_operations *ops)
 {
 
index e6b682c6c236b8152561496b2e00bba22e75e25c..06cec635e703adc3f9859ae8ca53dbb124820cd4 100644 (file)
@@ -120,7 +120,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)
        if (hub_is_superspeed(hub->hdev))
                return "5.0 Gb/s";
        if (portstatus & USB_PORT_STAT_HIGH_SPEED)
-               return "480 Mb/s";
+               return "480 Mb/s";
        else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                return "1.5 Mb/s";
        else
@@ -135,7 +135,7 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
        return usb_get_intfdata(hdev->actconfig->interface[0]);
 }
 
-static int usb_device_supports_lpm(struct usb_device *udev)
+int usb_device_supports_lpm(struct usb_device *udev)
 {
        /* USB 2.1 (and greater) devices indicate LPM support through
         * their USB 2.0 Extended Capabilities BOS descriptor.
@@ -156,6 +156,11 @@ static int usb_device_supports_lpm(struct usb_device *udev)
                                "Power management will be impacted.\n");
                return 0;
        }
+
+       /* udev is root hub */
+       if (!udev->parent)
+               return 1;
+
        if (udev->parent->lpm_capable)
                return 1;
 
@@ -310,9 +315,9 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
                return;
 
        udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
-       udev_u2_del = udev->bos->ss_cap->bU2DevExitLat;
+       udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
        hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
-       hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat;
+       hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);
 
        usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
                        hub, &udev->parent->u1_params, hub_u1_del);
@@ -433,7 +438,7 @@ static void set_port_led(
                        case HUB_LED_OFF: s = "off"; break;
                        case HUB_LED_AUTO: s = "auto"; break;
                        default: s = "??"; break;
-                       }; s; }),
+                       } s; }),
                        status);
 }
 
@@ -857,7 +862,7 @@ static int hub_hub_status(struct usb_hub *hub,
                                "%s failed (err = %d)\n", __func__, ret);
        } else {
                *status = le16_to_cpu(hub->status->hub.wHubStatus);
-               *change = le16_to_cpu(hub->status->hub.wHubChange); 
+               *change = le16_to_cpu(hub->status->hub.wHubChange);
                ret = 0;
        }
        mutex_unlock(&hub->status_mutex);
@@ -956,7 +961,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
         */
 
        set_bit(port1, hub->change_bits);
-       kick_khubd(hub);
+       kick_khubd(hub);
 }
 
 /**
@@ -1107,16 +1112,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        /*
                         * USB3 protocol ports will automatically transition
                         * to Enabled state when detect an USB3.0 device attach.
-                        * Do not disable USB3 protocol ports.
+                        * Do not disable USB3 protocol ports, just pretend
+                        * power was lost
                         */
-                       if (!hub_is_superspeed(hdev)) {
+                       portstatus &= ~USB_PORT_STAT_ENABLE;
+                       if (!hub_is_superspeed(hdev))
                                usb_clear_port_feature(hdev, port1,
                                                   USB_PORT_FEAT_ENABLE);
-                               portstatus &= ~USB_PORT_STAT_ENABLE;
-                       } else {
-                               /* Pretend that power was lost for USB3 devs */
-                               portstatus &= ~USB_PORT_STAT_ENABLE;
-                       }
                }
 
                /* Clear status-change flags; we'll debounce later */
@@ -1130,6 +1132,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        usb_clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_ENABLE);
                }
+               if (portchange & USB_PORT_STAT_C_RESET) {
+                       need_debounce_delay = true;
+                       usb_clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_RESET);
+               }
                if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
                                hub_is_superspeed(hub->hdev)) {
                        need_debounce_delay = true;
@@ -1361,7 +1368,7 @@ static int hub_configure(struct usb_hub *hub,
        if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
                        !(hub_is_superspeed(hdev))) {
                int     i;
-               char    portstr [USB_MAXCHILDREN + 1];
+               char    portstr[USB_MAXCHILDREN + 1];
 
                for (i = 0; i < hdev->maxchild; i++)
                        portstr[i] = hub->descriptor->u.hs.DeviceRemovable
@@ -1429,32 +1436,32 @@ static int hub_configure(struct usb_hub *hub,
 
        /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
        switch (wHubCharacteristics & HUB_CHAR_TTTT) {
-               case HUB_TTTT_8_BITS:
-                       if (hdev->descriptor.bDeviceProtocol != 0) {
-                               hub->tt.think_time = 666;
-                               dev_dbg(hub_dev, "TT requires at most %d "
-                                               "FS bit times (%d ns)\n",
-                                       8, hub->tt.think_time);
-                       }
-                       break;
-               case HUB_TTTT_16_BITS:
-                       hub->tt.think_time = 666 * 2;
-                       dev_dbg(hub_dev, "TT requires at most %d "
-                                       "FS bit times (%d ns)\n",
-                               16, hub->tt.think_time);
-                       break;
-               case HUB_TTTT_24_BITS:
-                       hub->tt.think_time = 666 * 3;
+       case HUB_TTTT_8_BITS:
+               if (hdev->descriptor.bDeviceProtocol != 0) {
+                       hub->tt.think_time = 666;
                        dev_dbg(hub_dev, "TT requires at most %d "
                                        "FS bit times (%d ns)\n",
-                               24, hub->tt.think_time);
-                       break;
-               case HUB_TTTT_32_BITS:
-                       hub->tt.think_time = 666 * 4;
-                       dev_dbg(hub_dev, "TT requires at most %d "
-                                       "FS bit times (%d ns)\n",
-                               32, hub->tt.think_time);
-                       break;
+                               8, hub->tt.think_time);
+               }
+               break;
+       case HUB_TTTT_16_BITS:
+               hub->tt.think_time = 666 * 2;
+               dev_dbg(hub_dev, "TT requires at most %d "
+                               "FS bit times (%d ns)\n",
+                       16, hub->tt.think_time);
+               break;
+       case HUB_TTTT_24_BITS:
+               hub->tt.think_time = 666 * 3;
+               dev_dbg(hub_dev, "TT requires at most %d "
+                               "FS bit times (%d ns)\n",
+                       24, hub->tt.think_time);
+               break;
+       case HUB_TTTT_32_BITS:
+               hub->tt.think_time = 666 * 4;
+               dev_dbg(hub_dev, "TT requires at most %d "
+                               "FS bit times (%d ns)\n",
+                       32, hub->tt.think_time);
+               break;
        }
 
        /* probe() zeroes hub->indicator[] */
@@ -1560,7 +1567,7 @@ static int hub_configure(struct usb_hub *hub,
 
        /* maybe cycle the hub leds */
        if (hub->has_indicators && blinkenlights)
-               hub->indicator [0] = INDICATOR_CYCLE;
+               hub->indicator[0] = INDICATOR_CYCLE;
 
        for (i = 0; i < hdev->maxchild; i++) {
                ret = usb_hub_create_port_device(hub, i + 1);
@@ -1978,7 +1985,7 @@ static void choose_devnum(struct usb_device *udev)
                if (devnum >= 128)
                        devnum = find_next_zero_bit(bus->devmap.devicemap,
                                                    128, 1);
-               bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+               bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
        }
        if (devnum < 128) {
                set_bit(devnum, bus->devmap.devicemap);
@@ -2018,8 +2025,8 @@ static void hub_free_dev(struct usb_device *udev)
  * Something got disconnected. Get rid of it and all of its children.
  *
  * If *pdev is a normal device then the parent hub must already be locked.
- * If *pdev is a root hub then this routine will acquire the
- * usb_bus_list_lock on behalf of the caller.
+ * If *pdev is a root hub then the caller must hold the usb_bus_list_lock,
+ * which protects the set of root hubs as well as the list of buses.
  *
  * Only hub drivers (including virtual root hub drivers for host
  * controllers) should ever call this.
@@ -2232,8 +2239,7 @@ static int usb_enumerate_device(struct usb_device *udev)
                udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
                udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
                udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-       }
-       else {
+       } else {
                /* read the standard strings and cache them if present */
                udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
                udev->manufacturer = usb_cache_string(udev,
@@ -2489,7 +2495,7 @@ error_device_descriptor:
        usb_autosuspend_device(usb_dev);
 error_autoresume:
 out_authorized:
-       usb_unlock_device(usb_dev);     // complements locktree
+       usb_unlock_device(usb_dev);     /* complements locktree */
        return result;
 }
 
@@ -3108,8 +3114,8 @@ static int finish_port_resume(struct usb_device *udev)
  retry_reset_resume:
                status = usb_reset_and_verify_device(udev);
 
-       /* 10.5.4.5 says be sure devices in the tree are still there.
-        * For now let's assume the device didn't go crazy on resume,
+       /* 10.5.4.5 says be sure devices in the tree are still there.
+        * For now let's assume the device didn't go crazy on resume,
         * and device drivers will know about any resume quirks.
         */
        if (status == 0) {
@@ -3211,7 +3217,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        if (status == 0 && !port_is_suspended(hub, portstatus))
                goto SuspendCleared;
 
-       // dev_dbg(hub->intfdev, "resume port %d\n", port1);
+       /* dev_dbg(hub->intfdev, "resume port %d\n", port1); */
 
        set_bit(port1, hub->busy_bits);
 
@@ -3855,7 +3861,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
  * Between connect detection and reset signaling there must be a delay
  * of 100ms at least for debounce and power-settling.  The corresponding
  * timer shall restart whenever the downstream port detects a disconnect.
- * 
+ *
  * Apparently there are some bluetooth and irda-dongles and a number of
  * low-speed devices for which this debounce period may last over a second.
  * Not covered by the spec - but easy to deal with.
@@ -3949,6 +3955,32 @@ static int hub_set_address(struct usb_device *udev, int devnum)
        return retval;
 }
 
+/*
+ * There are reports of USB 3.0 devices that say they support USB 2.0 Link PM
+ * when they're plugged into a USB 2.0 port, but they don't work when LPM is
+ * enabled.
+ *
+ * Only enable USB 2.0 Link PM if the port is internal (hardwired), or the
+ * device says it supports the new USB 2.0 Link PM errata by setting the BESL
+ * support bit in the BOS descriptor.
+ */
+static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
+{
+       int connect_type;
+
+       if (!udev->usb2_hw_lpm_capable)
+               return;
+
+       connect_type = usb_get_hub_port_connect_type(udev->parent,
+                       udev->portnum);
+
+       if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) ||
+                       connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
+               udev->usb2_hw_lpm_allowed = 1;
+               usb_set_usb2_hardware_lpm(udev, 1);
+       }
+}
+
 /* Reset device, (re)assign address, get device descriptor.
  * Device connection must be stable, no more debouncing needed.
  * Returns device in USB_STATE_ADDRESS, except on error.
@@ -4055,7 +4087,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                udev->tt = &hub->tt;
                udev->ttport = port1;
        }
+
        /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
         * Because device hardware and firmware is sometimes buggy in
         * this area, and this is how Linux has done it for ages.
@@ -4130,11 +4162,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 #undef GET_DESCRIPTOR_BUFSIZE
                }
 
-               /*
-                * If device is WUSB, we already assigned an
-                * unauthorized address in the Connect Ack sequence;
-                * authorization will assign the final address.
-                */
+               /*
+                * If device is WUSB, we already assigned an
+                * unauthorized address in the Connect Ack sequence;
+                * authorization will assign the final address.
+                */
                if (udev->wusb == 0) {
                        for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
                                retval = hub_set_address(udev, devnum);
@@ -4163,7 +4195,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                        msleep(10);
                        if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
                                break;
-               }
+               }
 
                retval = usb_get_device_descriptor(udev, 8);
                if (retval < 8) {
@@ -4219,7 +4251,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
                usb_ep0_reinit(udev);
        }
-  
+
        retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
        if (retval < (signed)sizeof(udev->descriptor)) {
                if (retval != -ENODEV)
@@ -4242,6 +4274,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        /* notify HCD that we have a device connected and addressed */
        if (hcd->driver->update_device)
                hcd->driver->update_device(hcd, udev);
+       hub_set_initial_usb2_lpm_policy(udev);
 fail:
        if (retval) {
                hub_port_disable(hub, port1, 0);
@@ -4316,7 +4349,7 @@ hub_power_remaining (struct usb_hub *hub)
        }
        if (remaining < 0) {
                dev_warn(hub->intfdev, "%dmA over power budget!\n",
-                       - remaining);
+                       -remaining);
                remaining = 0;
        }
        return remaining;
@@ -4427,7 +4460,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
                if (portstatus & USB_PORT_STAT_ENABLE)
-                       goto done;
+                       goto done;
                return;
        }
        if (hub_is_superspeed(hub->hdev))
@@ -4450,7 +4483,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
 
                usb_set_device_state(udev, USB_STATE_POWERED);
-               udev->bus_mA = hub->mA_per_port;
+               udev->bus_mA = hub->mA_per_port;
                udev->level = hdev->level + 1;
                udev->wusb = hub_is_wusb(hub);
 
@@ -4504,7 +4537,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                                goto loop_disable;
                        }
                }
+
                /* check for devices running slower than they could */
                if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
                                && udev->speed == USB_SPEED_FULL
@@ -4564,7 +4597,7 @@ loop:
                        dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
                                        port1);
        }
+
 done:
        hub_port_disable(hub, port1, 1);
        if (hcd->driver->relinquish_port && !hub->hdev->parent)
@@ -4729,7 +4762,7 @@ static void hub_events(void)
                                 * EM interference sometimes causes badly
                                 * shielded USB devices to be shutdown by
                                 * the hub, this hack enables them again.
-                                * Works at least with mouse driver. 
+                                * Works at least with mouse driver.
                                 */
                                if (!(portstatus & USB_PORT_STAT_ENABLE)
                                    && !connect_change
@@ -4841,7 +4874,7 @@ static void hub_events(void)
                                dev_dbg(hub_dev, "over-current change\n");
                                clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
                                msleep(500);    /* Cool down */
-                               hub_power_on(hub, true);
+                               hub_power_on(hub, true);
                                hub_hub_status(hub, &status, &unused);
                                if (status & HUB_STATUS_OVERCURRENT)
                                        dev_err(hub_dev, "over-current "
@@ -4861,7 +4894,7 @@ static void hub_events(void)
                usb_unlock_device(hdev);
                kref_put(&hub->kref, hub_release);
 
-        } /* end while (1) */
+       } /* end while (1) */
 }
 
 static int hub_thread(void *__unused)
@@ -4886,7 +4919,7 @@ static int hub_thread(void *__unused)
 
 static const struct usb_device_id hub_id_table[] = {
     { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
-                  | USB_DEVICE_ID_MATCH_INT_CLASS,
+                       | USB_DEVICE_ID_MATCH_INT_CLASS,
       .idVendor = USB_VENDOR_GENESYS_LOGIC,
       .bInterfaceClass = USB_CLASS_HUB,
       .driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
@@ -5086,6 +5119,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        }
        parent_hub = usb_hub_to_struct_hub(parent_hdev);
 
+       /* Disable USB2 hardware LPM.
+        * It will be re-enabled by the enumeration process.
+        */
+       if (udev->usb2_hw_lpm_enabled == 1)
+               usb_set_usb2_hardware_lpm(udev, 0);
+
        bos = udev->bos;
        udev->bos = NULL;
 
@@ -5120,13 +5159,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
 
        if (ret < 0)
                goto re_enumerate;
+
        /* Device might have changed firmware (DFU or similar) */
        if (descriptors_changed(udev, &descriptor, bos)) {
                dev_info(&udev->dev, "device firmware changed\n");
                udev->descriptor = descriptor;  /* for disconnect() calls */
                goto re_enumerate;
-       }
+       }
 
        /* Restore the device's previous configuration */
        if (!udev->actconfig)
@@ -5151,7 +5190,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                        udev->actconfig->desc.bConfigurationValue, ret);
                mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
-       }
+       }
        mutex_unlock(hcd->bandwidth_mutex);
        usb_set_device_state(udev, USB_STATE_CONFIGURED);
 
@@ -5193,12 +5232,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
 
 done:
        /* Now that the alt settings are re-installed, enable LTM and LPM. */
+       usb_set_usb2_hardware_lpm(udev, 1);
        usb_unlocked_enable_lpm(udev);
        usb_enable_ltm(udev);
        usb_release_bos_descriptor(udev);
        udev->bos = bos;
        return 0;
+
 re_enumerate:
        /* LPM state doesn't matter when we're about to destroy the device. */
        hub_port_logical_disconnect(parent_hub, port1);
index 82927e1ed27d078f513a69727ebf7e55ef885ad8..bb315970e475e3bd90b5985c45a0afc0daf61fd2 100644 (file)
@@ -1182,8 +1182,12 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
                        put_device(&dev->actconfig->interface[i]->dev);
                        dev->actconfig->interface[i] = NULL;
                }
+
+               if (dev->usb2_hw_lpm_enabled == 1)
+                       usb_set_usb2_hardware_lpm(dev, 0);
                usb_unlocked_disable_lpm(dev);
                usb_disable_ltm(dev);
+
                dev->actconfig = NULL;
                if (dev->state == USB_STATE_CONFIGURED)
                        usb_set_device_state(dev, USB_STATE_ADDRESS);
index 01fe36273f3b144cb1c1fe3306ebdf16459c605f..12924dbfdc2cb2c6b18319f4c4dec2679e3ae1bc 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/usb.h>
 #include <linux/usb/quirks.h>
+#include <linux/usb/hcd.h>
 #include "usb.h"
 
 /* Lists of quirky USB devices, split in device quirks and interface quirks.
@@ -161,6 +162,21 @@ static const struct usb_device_id usb_interface_quirk_list[] = {
        { }  /* terminating entry must be last */
 };
 
+static const struct usb_device_id usb_amd_resume_quirk_list[] = {
+       /* Lenovo Mouse with Pixart controller */
+       { USB_DEVICE(0x17ef, 0x602e), .driver_info = USB_QUIRK_RESET_RESUME },
+
+       /* Pixart Mouse */
+       { USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME },
+       { USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME },
+       { USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME },
+
+       /* Logitech Optical Mouse M90/M100 */
+       { USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME },
+
+       { }  /* terminating entry must be last */
+};
+
 static bool usb_match_any_interface(struct usb_device *udev,
                                    const struct usb_device_id *id)
 {
@@ -187,6 +203,18 @@ static bool usb_match_any_interface(struct usb_device *udev,
        return false;
 }
 
+static int usb_amd_resume_quirk(struct usb_device *udev)
+{
+       struct usb_hcd *hcd;
+
+       hcd = bus_to_hcd(udev->bus);
+       /* The device should be attached directly to root hub */
+       if (udev->level == 1 && hcd->amd_resume_bug == 1)
+               return 1;
+
+       return 0;
+}
+
 static u32 __usb_detect_quirks(struct usb_device *udev,
                               const struct usb_device_id *id)
 {
@@ -212,6 +240,15 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
 void usb_detect_quirks(struct usb_device *udev)
 {
        udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
+
+       /*
+        * Pixart-based mice would trigger remote wakeup issue on AMD
+        * Yangtze chipset, so set them as RESET_RESUME flag.
+        */
+       if (usb_amd_resume_quirk(udev))
+               udev->quirks |= __usb_detect_quirks(udev,
+                               usb_amd_resume_quirk_list);
+
        if (udev->quirks)
                dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
                        udev->quirks);
index 6d2c8edb1ffe939e8c31b4a1eeb4585b27be619b..52a97adf02a04dcb39e866a131bfdfa5053efe51 100644 (file)
@@ -23,14 +23,16 @@ static ssize_t field##_show(struct device *dev,                             \
 {                                                                      \
        struct usb_device *udev;                                        \
        struct usb_host_config *actconfig;                              \
+       ssize_t rc = 0;                                                 \
                                                                        \
        udev = to_usb_device(dev);                                      \
+       usb_lock_device(udev);                                          \
        actconfig = udev->actconfig;                                    \
        if (actconfig)                                                  \
-               return sprintf(buf, format_string,                      \
+               rc = sprintf(buf, format_string,                        \
                                actconfig->desc.field);                 \
-       else                                                            \
-               return 0;                                               \
+       usb_unlock_device(udev);                                        \
+       return rc;                                                      \
 }                                                                      \
 
 #define usb_actconfig_attr(field, format_string)               \
@@ -45,12 +47,15 @@ static ssize_t bMaxPower_show(struct device *dev,
 {
        struct usb_device *udev;
        struct usb_host_config *actconfig;
+       ssize_t rc = 0;
 
        udev = to_usb_device(dev);
+       usb_lock_device(udev);
        actconfig = udev->actconfig;
-       if (!actconfig)
-               return 0;
-       return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+       if (actconfig)
+               rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+       usb_unlock_device(udev);
+       return rc;
 }
 static DEVICE_ATTR_RO(bMaxPower);
 
@@ -59,12 +64,15 @@ static ssize_t configuration_show(struct device *dev,
 {
        struct usb_device *udev;
        struct usb_host_config *actconfig;
+       ssize_t rc = 0;
 
        udev = to_usb_device(dev);
+       usb_lock_device(udev);
        actconfig = udev->actconfig;
-       if ((!actconfig) || (!actconfig->string))
-               return 0;
-       return sprintf(buf, "%s\n", actconfig->string);
+       if (actconfig && actconfig->string)
+               rc = sprintf(buf, "%s\n", actconfig->string);
+       usb_unlock_device(udev);
+       return rc;
 }
 static DEVICE_ATTR_RO(configuration);
 
@@ -390,7 +398,8 @@ static DEVICE_ATTR_RW(autosuspend);
 static const char on_string[] = "on";
 static const char auto_string[] = "auto";
 
-static void warn_level(void) {
+static void warn_level(void)
+{
        static int level_warned;
 
        if (!level_warned) {
@@ -449,7 +458,7 @@ static ssize_t usb2_hardware_lpm_show(struct device *dev,
        struct usb_device *udev = to_usb_device(dev);
        const char *p;
 
-       if (udev->usb2_hw_lpm_enabled == 1)
+       if (udev->usb2_hw_lpm_allowed == 1)
                p = "enabled";
        else
                p = "disabled";
@@ -469,8 +478,10 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev,
 
        ret = strtobool(buf, &value);
 
-       if (!ret)
+       if (!ret) {
+               udev->usb2_hw_lpm_allowed = value;
                ret = usb_set_usb2_hardware_lpm(udev, value);
+       }
 
        usb_unlock_device(udev);
 
@@ -644,7 +655,7 @@ static ssize_t authorized_store(struct device *dev,
                result = usb_deauthorize_device(usb_dev);
        else
                result = usb_authorize_device(usb_dev);
-       return result < 0? result : size;
+       return result < 0 ? result : size;
 }
 static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
                                  authorized_show, authorized_store);
@@ -764,6 +775,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
         * Following that are the raw descriptor entries for all the
         * configurations (config plus subsidiary descriptors).
         */
+       usb_lock_device(udev);
        for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
                        nleft > 0; ++cfgno) {
                if (cfgno < 0) {
@@ -784,6 +796,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
                        off -= srclen;
                }
        }
+       usb_unlock_device(udev);
        return count - nleft;
 }
 
@@ -870,9 +883,7 @@ static ssize_t interface_show(struct device *dev, struct device_attribute *attr,
        char *string;
 
        intf = to_usb_interface(dev);
-       string = intf->cur_altsetting->string;
-       barrier();              /* The altsetting might change! */
-
+       string = ACCESS_ONCE(intf->cur_altsetting->string);
        if (!string)
                return 0;
        return sprintf(buf, "%s\n", string);
@@ -888,7 +899,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 
        intf = to_usb_interface(dev);
        udev = interface_to_usbdev(intf);
-       alt = intf->cur_altsetting;
+       alt = ACCESS_ONCE(intf->cur_altsetting);
 
        return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
                        "ic%02Xisc%02Xip%02Xin%02X\n",
@@ -909,23 +920,14 @@ static ssize_t supports_autosuspend_show(struct device *dev,
                                         struct device_attribute *attr,
                                         char *buf)
 {
-       struct usb_interface *intf;
-       struct usb_device *udev;
-       int ret;
+       int s;
 
-       intf = to_usb_interface(dev);
-       udev = interface_to_usbdev(intf);
-
-       usb_lock_device(udev);
+       device_lock(dev);
        /* Devices will be autosuspended even when an interface isn't claimed */
-       if (!intf->dev.driver ||
-                       to_usb_driver(intf->dev.driver)->supports_autosuspend)
-               ret = sprintf(buf, "%u\n", 1);
-       else
-               ret = sprintf(buf, "%u\n", 0);
-       usb_unlock_device(udev);
+       s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
+       device_unlock(dev);
 
-       return ret;
+       return sprintf(buf, "%u\n", s);
 }
 static DEVICE_ATTR_RO(supports_autosuspend);
 
index c12bc790a6a702de065087b9d4c07563f3bb54b1..e62208356c8915d6fc565173f4561500cb5261a7 100644 (file)
@@ -138,13 +138,19 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
 }
 EXPORT_SYMBOL_GPL(usb_anchor_urb);
 
+static int usb_anchor_check_wakeup(struct usb_anchor *anchor)
+{
+       return atomic_read(&anchor->suspend_wakeups) == 0 &&
+               list_empty(&anchor->urb_list);
+}
+
 /* Callers must hold anchor->lock */
 static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
 {
        urb->anchor = NULL;
        list_del(&urb->anchor_list);
        usb_put_urb(urb);
-       if (list_empty(&anchor->urb_list))
+       if (usb_anchor_check_wakeup(anchor))
                wake_up(&anchor->wait);
 }
 
@@ -845,6 +851,39 @@ void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 
+/**
+ * usb_anchor_suspend_wakeups
+ * @anchor: the anchor you want to suspend wakeups on
+ *
+ * Call this to stop the last urb being unanchored from waking up any
+ * usb_wait_anchor_empty_timeout waiters. This is used in the hcd urb give-
+ * back path to delay waking up until after the completion handler has run.
+ */
+void usb_anchor_suspend_wakeups(struct usb_anchor *anchor)
+{
+       if (anchor)
+               atomic_inc(&anchor->suspend_wakeups);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_suspend_wakeups);
+
+/**
+ * usb_anchor_resume_wakeups
+ * @anchor: the anchor you want to resume wakeups on
+ *
+ * Allow usb_wait_anchor_empty_timeout waiters to be woken up again, and
+ * wake up any current waiters if the anchor is empty.
+ */
+void usb_anchor_resume_wakeups(struct usb_anchor *anchor)
+{
+       if (!anchor)
+               return;
+
+       atomic_dec(&anchor->suspend_wakeups);
+       if (usb_anchor_check_wakeup(anchor))
+               wake_up(&anchor->wait);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_resume_wakeups);
+
 /**
  * usb_wait_anchor_empty_timeout - wait for an anchor to be unused
  * @anchor: the anchor you want to become unused
@@ -858,7 +897,8 @@ EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
                                  unsigned int timeout)
 {
-       return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
+       return wait_event_timeout(anchor->wait,
+                                 usb_anchor_check_wakeup(anchor),
                                  msecs_to_jiffies(timeout));
 }
 EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
index 0a6ee2e70b25afecaae6dfce65a03fe22e40ca04..4d1144990d4c848362c2f86cf8aa1039c3d70923 100644 (file)
@@ -497,7 +497,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
                dev->authorized = 1;
        else {
                dev->authorized = usb_hcd->authorized_default;
-               dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
+               dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
        }
        return dev;
 }
index 823857767a16f3384730dcf1f90c42f8eedc3588..c49383669cd87ce4dda9b996fd9b3f1caf03bd2b 100644 (file)
@@ -35,6 +35,7 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
                unsigned int size);
 extern int usb_get_bos_descriptor(struct usb_device *dev);
 extern void usb_release_bos_descriptor(struct usb_device *dev);
+extern int usb_device_supports_lpm(struct usb_device *udev);
 extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_choose_configuration(struct usb_device *udev);
index 474162e9d01d2e03bfaf52402a8cfc1b4461c304..74f9cf02da070a6d1c23b643a56f76e3264fb8cd 100644 (file)
@@ -584,7 +584,7 @@ static int dwc3_remove(struct platform_device *pdev)
        usb_phy_set_suspend(dwc->usb2_phy, 1);
        usb_phy_set_suspend(dwc->usb3_phy, 1);
 
-       pm_runtime_put(&pdev->dev);
+       pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
        dwc3_debugfs_exit(dwc);
@@ -691,7 +691,6 @@ static int dwc3_resume(struct device *dev)
 
        usb_phy_init(dwc->usb3_phy);
        usb_phy_init(dwc->usb2_phy);
-       msleep(100);
 
        spin_lock_irqsave(&dwc->lock, flags);
 
index 2e252aae51ca0bcc5da41b3251c45cec91cced7d..31443aeedcdbf374a6d663e69087ba18f9c37612 100644 (file)
@@ -165,7 +165,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
        return 0;
 
 err3:
-       pci_set_drvdata(pci, NULL);
        platform_device_put(dwc3);
 err1:
        pci_disable_device(pci);
@@ -180,7 +179,6 @@ static void dwc3_pci_remove(struct pci_dev *pci)
        platform_device_unregister(glue->dwc3);
        platform_device_unregister(glue->usb2_phy);
        platform_device_unregister(glue->usb3_phy);
-       pci_set_drvdata(pci, NULL);
        pci_disable_device(pci);
 }
 
index 7fa93f4bc507de41782486301760506e18f44a03..95f7649c71a78745692a63bafd1527daacda52e3 100644 (file)
@@ -352,7 +352,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        response_pkt = (__le16 *) dwc->setup_buf;
        *response_pkt = cpu_to_le16(usb_status);
@@ -470,7 +470,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
 
        default:
                return -EINVAL;
-       };
+       }
 
        return 0;
 }
@@ -709,7 +709,7 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
                dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
                ret = dwc3_ep0_delegate_req(dwc, ctrl);
                break;
-       };
+       }
 
        return ret;
 }
index 5e29ddeb4d33d0c65f29116dca2ddbe42ea5407f..8cfc3191be50aee8f9157ba95e84c49446d1ae55 100644 (file)
@@ -568,10 +568,6 @@ try_again:
                dbgp_printk("Could not find attached debug device\n");
                goto err;
        }
-       if (ret < 0) {
-               dbgp_printk("Attached device is not a debug device\n");
-               goto err;
-       }
        dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
        dbgp_endpoint_in = dbgp_desc.bDebugInEndpoint;
 
index 48cddf3cd6b889b420d45da418e852ecd671d2c5..a91e6422f93021f912042298a99cdde36362b09b 100644 (file)
@@ -58,6 +58,20 @@ config USB_GADGET_DEBUG
           trying to track down.  Never enable these messages for a
           production build.
 
+config USB_GADGET_VERBOSE
+       bool "Verbose debugging Messages (DEVELOPMENT)"
+       depends on USB_GADGET_DEBUG
+       help
+          Many controller and gadget drivers will print verbose debugging
+          messages if you use this option to ask for those messages.
+
+          Avoid enabling these messages, even if you're actively
+          debugging such a driver.  Many drivers will emit so many
+          messages that the driver timings are affected, which will
+          either create new failure modes or remove the one you're
+          trying to track down.  Never enable these messages for a
+          production build.
+
 config USB_GADGET_DEBUG_FILES
        boolean "Debugging information files (DEVELOPMENT)"
        depends on PROC_FS
@@ -525,6 +539,9 @@ config USB_F_SUBSET
 config USB_F_RNDIS
        tristate
 
+config USB_F_MASS_STORAGE
+       tristate
+
 choice
        tristate "USB Gadget Drivers"
        default USB_ETH
@@ -662,6 +679,16 @@ config USB_CONFIGFS_PHONET
        help
          The Phonet protocol implementation for USB device.
 
+config USB_CONFIGFS_MASS_STORAGE
+       boolean "Mass storage"
+       depends on USB_CONFIGFS
+       select USB_F_MASS_STORAGE
+       help
+         The Mass Storage Gadget acts as a USB Mass Storage disk drive.
+         As its storage repository it can use a regular file or a block
+         device (in much the same way as the "loop" device driver),
+         specified as a module parameter or sysfs option.
+
 config USB_ZERO
        tristate "Gadget Zero (DEVELOPMENT)"
        select USB_LIBCOMPOSITE
@@ -878,6 +905,7 @@ config USB_MASS_STORAGE
        tristate "Mass Storage Gadget"
        depends on BLOCK
        select USB_LIBCOMPOSITE
+       select USB_F_MASS_STORAGE
        help
          The Mass Storage Gadget acts as a USB Mass Storage disk drive.
          As its storage repository it can use a regular file or a block
@@ -1001,6 +1029,7 @@ config USB_G_ACM_MS
        select USB_LIBCOMPOSITE
        select USB_U_SERIAL
        select USB_F_ACM
+       select USB_F_MASS_STORAGE
        help
          This driver provides two functions in one configuration:
          a mass storage, and a CDC ACM (serial port) link.
@@ -1015,8 +1044,8 @@ config USB_G_MULTI
        select USB_LIBCOMPOSITE
        select USB_U_SERIAL
        select USB_U_ETHER
-       select USB_U_RNDIS
        select USB_F_ACM
+       select USB_F_MASS_STORAGE
        help
          The Multifunction Composite Gadget provides Ethernet (RNDIS
          and/or CDC Ethernet), mass storage and ACM serial link
@@ -1035,6 +1064,8 @@ config USB_G_MULTI
 config USB_G_MULTI_RNDIS
        bool "RNDIS + CDC Serial + Storage configuration"
        depends on USB_G_MULTI
+       select USB_U_RNDIS
+       select USB_F_RNDIS
        default y
        help
          This option enables a configuration with RNDIS, CDC Serial and
@@ -1048,6 +1079,7 @@ config USB_G_MULTI_CDC
        bool "CDC Ethernet + CDC Serial + Storage configuration"
        depends on USB_G_MULTI
        default n
+       select USB_F_ECM
        help
          This option enables a configuration with CDC Ethernet (ECM), CDC
          Serial and Mass Storage functions available in the Multifunction
index 386db9daf1d9a254528deb6bd2273ef92b981e94..f1af39603d4d2f4132a06e346ab3772bf12999ee 100644 (file)
@@ -1,7 +1,8 @@
 #
 # USB peripheral controller drivers
 #
-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
+ccflags-$(CONFIG_USB_GADGET_DEBUG)     := -DDEBUG
+ccflags-$(CONFIG_USB_GADGET_VERBOSE)   += -DVERBOSE_DEBUG
 
 obj-$(CONFIG_USB_GADGET)       += udc-core.o
 obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
@@ -60,6 +61,8 @@ usb_f_ecm_subset-y            := f_subset.o
 obj-$(CONFIG_USB_F_SUBSET)     += usb_f_ecm_subset.o
 usb_f_rndis-y                  := f_rndis.o
 obj-$(CONFIG_USB_F_RNDIS)      += usb_f_rndis.o
+usb_f_mass_storage-y           := f_mass_storage.o storage_common.o
+obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
 
 #
 # USB gadget drivers
index 4b947bb50f62acdd17bebee987b5b14faa26e253..7bfa134fe0e3bd56e84b8adb16160b4124b20f21 100644 (file)
 #define ACM_MS_VENDOR_NUM      0x1d6b  /* Linux Foundation */
 #define ACM_MS_PRODUCT_NUM     0x0106  /* Composite Gadget: ACM + MS*/
 
-/*-------------------------------------------------------------------------*/
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module.  So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "f_mass_storage.c"
+#include "f_mass_storage.h"
 
 /*-------------------------------------------------------------------------*/
 USB_GADGET_COMPOSITE_OPTIONS();
@@ -104,18 +95,35 @@ static struct usb_gadget_strings *dev_strings[] = {
 /****************************** Configurations ******************************/
 
 static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
-FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
 
-static struct fsg_common fsg_common;
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers        CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_DEBUG */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
 
 /*-------------------------------------------------------------------------*/
 static struct usb_function *f_acm;
 static struct usb_function_instance *f_acm_inst;
+
+static struct usb_function_instance *fi_msg;
+static struct usb_function *f_msg;
+
 /*
  * We _always_ have both ACM and mass storage functions.
  */
 static int __init acm_ms_do_config(struct usb_configuration *c)
 {
+       struct fsg_opts *opts;
        int     status;
 
        if (gadget_is_otg(c->cdev->gadget)) {
@@ -123,31 +131,37 @@ static int __init acm_ms_do_config(struct usb_configuration *c)
                c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
        }
 
-       f_acm_inst = usb_get_function_instance("acm");
-       if (IS_ERR(f_acm_inst))
-               return PTR_ERR(f_acm_inst);
+       opts = fsg_opts_from_func_inst(fi_msg);
 
        f_acm = usb_get_function(f_acm_inst);
-       if (IS_ERR(f_acm)) {
-               status = PTR_ERR(f_acm);
-               goto err_func;
+       if (IS_ERR(f_acm))
+               return PTR_ERR(f_acm);
+
+       f_msg = usb_get_function(fi_msg);
+       if (IS_ERR(f_msg)) {
+               status = PTR_ERR(f_msg);
+               goto put_acm;
        }
 
        status = usb_add_function(c, f_acm);
        if (status < 0)
-               goto err_conf;
+               goto put_msg;
 
-       status = fsg_bind_config(c->cdev, c, &fsg_common);
-       if (status < 0)
-               goto err_fsg;
+       status = fsg_common_run_thread(opts->common);
+       if (status)
+               goto remove_acm;
+
+       status = usb_add_function(c, f_msg);
+       if (status)
+               goto remove_acm;
 
        return 0;
-err_fsg:
+remove_acm:
        usb_remove_function(c, f_acm);
-err_conf:
+put_msg:
+       usb_put_function(f_msg);
+put_acm:
        usb_put_function(f_acm);
-err_func:
-       usb_put_function_instance(f_acm_inst);
        return status;
 }
 
@@ -163,45 +177,82 @@ static struct usb_configuration acm_ms_config_driver = {
 static int __init acm_ms_bind(struct usb_composite_dev *cdev)
 {
        struct usb_gadget       *gadget = cdev->gadget;
+       struct fsg_opts         *opts;
+       struct fsg_config       config;
        int                     status;
-       void                    *retp;
 
-       /* set up mass storage function */
-       retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
-       if (IS_ERR(retp)) {
-               status = PTR_ERR(retp);
-               return PTR_ERR(retp);
+       f_acm_inst = usb_get_function_instance("acm");
+       if (IS_ERR(f_acm_inst))
+               return PTR_ERR(f_acm_inst);
+
+       fi_msg = usb_get_function_instance("mass_storage");
+       if (IS_ERR(fi_msg)) {
+               status = PTR_ERR(fi_msg);
+               goto fail_get_msg;
        }
 
+       /* set up mass storage function */
+       fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
+       opts = fsg_opts_from_func_inst(fi_msg);
+
+       opts->no_configfs = true;
+       status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
+       if (status)
+               goto fail;
+
+       status = fsg_common_set_nluns(opts->common, config.nluns);
+       if (status)
+               goto fail_set_nluns;
+
+       status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
+       if (status)
+               goto fail_set_cdev;
+
+       fsg_common_set_sysfs(opts->common, true);
+       status = fsg_common_create_luns(opts->common, &config);
+       if (status)
+               goto fail_set_cdev;
+
+       fsg_common_set_inquiry_string(opts->common, config.vendor_name,
+                                     config.product_name);
        /*
         * Allocate string descriptor numbers ... note that string
         * contents can be overridden by the composite_dev glue.
         */
        status = usb_string_ids_tab(cdev, strings_dev);
        if (status < 0)
-               goto fail1;
+               goto fail_string_ids;
        device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
        device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
 
        /* register our configuration */
        status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
        if (status < 0)
-               goto fail1;
+               goto fail_string_ids;
 
        usb_composite_overwrite_options(cdev, &coverwrite);
        dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
                        DRIVER_DESC);
-       fsg_common_put(&fsg_common);
        return 0;
 
        /* error recovery */
-fail1:
-       fsg_common_put(&fsg_common);
+fail_string_ids:
+       fsg_common_remove_luns(opts->common);
+fail_set_cdev:
+       fsg_common_free_luns(opts->common);
+fail_set_nluns:
+       fsg_common_free_buffers(opts->common);
+fail:
+       usb_put_function_instance(fi_msg);
+fail_get_msg:
+       usb_put_function_instance(f_acm_inst);
        return status;
 }
 
 static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
 {
+       usb_put_function(f_msg);
+       usb_put_function_instance(fi_msg);
        usb_put_function(f_acm);
        usb_put_function_instance(f_acm_inst);
        return 0;
index a9a4346c83aae221776a297eff8330aab6bb733c..54a1e2954cea9d53384085e04b91d65418fb575e 100644 (file)
@@ -3078,8 +3078,6 @@ static void udc_pci_remove(struct pci_dev *pdev)
        if (dev->active)
                pci_disable_device(pdev);
 
-       pci_set_drvdata(pdev, NULL);
-
        udc_remove(dev);
 }
 
index d4f0f3305759876a2fc1e81cb96e0ade69918eca..3e7ae707f691c4b0cf4b701024d8df0af4a5bb69 100644 (file)
@@ -354,7 +354,7 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
                return DIV_ROUND_UP(val, 8);
        default:
                return DIV_ROUND_UP(val, 2);
-       };
+       }
 }
 
 static int config_buf(struct usb_configuration *config,
index 8f0d6141e5e6c2b2c1acfb53b769438f32d7907a..25885112fa35b0cc5b0b25733c91d91365606fb8 100644 (file)
@@ -557,7 +557,7 @@ static struct config_group *function_make(
 
        fi = usb_get_function_instance(func_name);
        if (IS_ERR(fi))
-               return ERR_PTR(PTR_ERR(fi));
+               return ERR_CAST(fi);
 
        ret = config_item_set_name(&fi->group.cg_item, name);
        if (ret) {
@@ -991,6 +991,14 @@ static struct configfs_subsystem gadget_subsys = {
        .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex),
 };
 
+void unregister_gadget_item(struct config_item *item)
+{
+       struct gadget_info *gi = to_gadget_info(item);
+
+       unregister_gadget(gi);
+}
+EXPORT_SYMBOL(unregister_gadget_item);
+
 static int __init gadget_cfs_init(void)
 {
        int ret;
diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h
new file mode 100644 (file)
index 0000000..a7b564a
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef USB__GADGET__CONFIGFS__H
+#define USB__GADGET__CONFIGFS__H
+
+void unregister_gadget_item(struct config_item *item);
+
+#endif /*  USB__GADGET__CONFIGFS__H */
index b8a2376971a47aa357794117f74390aaee01a594..8f4dae3109235624e9e60c04a837aab7f6571e4a 100644 (file)
@@ -544,7 +544,7 @@ static int dummy_enable(struct usb_ep *_ep,
                 default:
                         val = "ctrl";
                         break;
-                }; val; }),
+                } val; }),
                max, ep->stream_en ? "enabled" : "disabled");
 
        /* at this point real hardware should be NAKing transfers
@@ -2271,7 +2271,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
                default:
                        s = "?";
                        break;
-                }; s; }),
+                } s; }),
                ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "",
                ({ char *s; \
                switch (usb_pipetype(urb->pipe)) { \
@@ -2287,7 +2287,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
                default: \
                        s = "-iso"; \
                        break; \
-               }; s; }),
+               } s; }),
                urb->actual_length, urb->transfer_buffer_length);
 }
 
index a01d7d38c01685135a503885342be7f9ec63ebbd..a03ba2c83589ee15f9c1880d17f1aa133eb47f29 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/freezer.h>
+#include <linux/module.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/composite.h>
 
 #include "gadget_chips.h"
+#include "configfs.h"
 
 
 /*------------------------------------------------------------------------*/
 
 static const char fsg_string_interface[] = "Mass Storage";
 
-#include "storage_common.c"
+#include "storage_common.h"
+#include "f_mass_storage.h"
 
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string               fsg_strings[] = {
+       {FSG_STRING_INTERFACE,          fsg_string_interface},
+       {}
+};
+
+static struct usb_gadget_strings       fsg_stringtab = {
+       .language       = 0x0409,               /* en-us */
+       .strings        = fsg_strings,
+};
+
+static struct usb_gadget_strings *fsg_strings_array[] = {
+       &fsg_stringtab,
+       NULL,
+};
 
 /*-------------------------------------------------------------------------*/
 
 struct fsg_dev;
 struct fsg_common;
 
-/* FSF callback functions */
-struct fsg_operations {
-       /*
-        * Callback function to call when thread exits.  If no
-        * callback is set or it returns value lower then zero MSF
-        * will force eject all LUNs it operates on (including those
-        * marked as non-removable or with prevent_medium_removal flag
-        * set).
-        */
-       int (*thread_exits)(struct fsg_common *common);
-};
-
 /* Data shared by all the FSG instances. */
 struct fsg_common {
        struct usb_gadget       *gadget;
@@ -268,13 +274,14 @@ struct fsg_common {
        struct fsg_buffhd       *next_buffhd_to_fill;
        struct fsg_buffhd       *next_buffhd_to_drain;
        struct fsg_buffhd       *buffhds;
+       unsigned int            fsg_num_buffers;
 
        int                     cmnd_size;
        u8                      cmnd[MAX_COMMAND_SIZE];
 
        unsigned int            nluns;
        unsigned int            lun;
-       struct fsg_lun          *luns;
+       struct fsg_lun          **luns;
        struct fsg_lun          *curlun;
 
        unsigned int            bulk_out_maxpacket;
@@ -294,6 +301,7 @@ struct fsg_common {
        unsigned int            short_packet_received:1;
        unsigned int            bad_lun_okay:1;
        unsigned int            running:1;
+       unsigned int            sysfs:1;
 
        int                     thread_wakeup_needed;
        struct completion       thread_notifier;
@@ -313,27 +321,6 @@ struct fsg_common {
        struct kref             ref;
 };
 
-struct fsg_config {
-       unsigned nluns;
-       struct fsg_lun_config {
-               const char *filename;
-               char ro;
-               char removable;
-               char cdrom;
-               char nofua;
-       } luns[FSG_MAX_LUNS];
-
-       /* Callback functions. */
-       const struct fsg_operations     *ops;
-       /* Gadget's private data. */
-       void                    *private_data;
-
-       const char *vendor_name;                /*  8 characters or less */
-       const char *product_name;               /* 16 characters or less */
-
-       char                    can_stall;
-};
-
 struct fsg_dev {
        struct usb_function     function;
        struct usb_gadget       *gadget;        /* Copy of cdev->gadget */
@@ -2172,7 +2159,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
                common->data_dir = DATA_DIR_NONE;
        common->lun = cbw->Lun;
        if (common->lun < common->nluns)
-               common->curlun = &common->luns[common->lun];
+               common->curlun = common->luns[common->lun];
        else
                common->curlun = NULL;
        common->tag = cbw->Tag;
@@ -2244,7 +2231,7 @@ reset:
        if (common->fsg) {
                fsg = common->fsg;
 
-               for (i = 0; i < fsg_num_buffers; ++i) {
+               for (i = 0; i < common->fsg_num_buffers; ++i) {
                        struct fsg_buffhd *bh = &common->buffhds[i];
 
                        if (bh->inreq) {
@@ -2303,7 +2290,7 @@ reset:
        clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
 
        /* Allocate the requests */
-       for (i = 0; i < fsg_num_buffers; ++i) {
+       for (i = 0; i < common->fsg_num_buffers; ++i) {
                struct fsg_buffhd       *bh = &common->buffhds[i];
 
                rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
@@ -2320,7 +2307,9 @@ reset:
 
        common->running = 1;
        for (i = 0; i < common->nluns; ++i)
-               common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+               if (common->luns[i])
+                       common->luns[i]->unit_attention_data =
+                               SS_RESET_OCCURRED;
        return rc;
 }
 
@@ -2372,7 +2361,7 @@ static void handle_exception(struct fsg_common *common)
 
        /* Cancel all the pending transfers */
        if (likely(common->fsg)) {
-               for (i = 0; i < fsg_num_buffers; ++i) {
+               for (i = 0; i < common->fsg_num_buffers; ++i) {
                        bh = &common->buffhds[i];
                        if (bh->inreq_busy)
                                usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
@@ -2384,7 +2373,7 @@ static void handle_exception(struct fsg_common *common)
                /* Wait until everything is idle */
                for (;;) {
                        int num_active = 0;
-                       for (i = 0; i < fsg_num_buffers; ++i) {
+                       for (i = 0; i < common->fsg_num_buffers; ++i) {
                                bh = &common->buffhds[i];
                                num_active += bh->inreq_busy + bh->outreq_busy;
                        }
@@ -2407,7 +2396,7 @@ static void handle_exception(struct fsg_common *common)
         */
        spin_lock_irq(&common->lock);
 
-       for (i = 0; i < fsg_num_buffers; ++i) {
+       for (i = 0; i < common->fsg_num_buffers; ++i) {
                bh = &common->buffhds[i];
                bh->state = BUF_STATE_EMPTY;
        }
@@ -2420,7 +2409,9 @@ static void handle_exception(struct fsg_common *common)
                common->state = FSG_STATE_STATUS_PHASE;
        else {
                for (i = 0; i < common->nluns; ++i) {
-                       curlun = &common->luns[i];
+                       curlun = common->luns[i];
+                       if (!curlun)
+                               continue;
                        curlun->prevent_medium_removal = 0;
                        curlun->sense_data = SS_NO_SENSE;
                        curlun->unit_attention_data = SS_NO_SENSE;
@@ -2462,8 +2453,9 @@ static void handle_exception(struct fsg_common *common)
                 * CONFIG_CHANGE cases.
                 */
                /* for (i = 0; i < common->nluns; ++i) */
-               /*      common->luns[i].unit_attention_data = */
-               /*              SS_RESET_OCCURRED;  */
+               /*      if (common->luns[i]) */
+               /*              common->luns[i]->unit_attention_data = */
+               /*                      SS_RESET_OCCURRED;  */
                break;
 
        case FSG_STATE_CONFIG_CHANGE:
@@ -2559,12 +2551,13 @@ static int fsg_main_thread(void *common_)
 
        if (!common->ops || !common->ops->thread_exits
         || common->ops->thread_exits(common) < 0) {
-               struct fsg_lun *curlun = common->luns;
+               struct fsg_lun **curlun_it = common->luns;
                unsigned i = common->nluns;
 
                down_write(&common->filesem);
-               for (; i--; ++curlun) {
-                       if (!fsg_lun_is_open(curlun))
+               for (; i--; ++curlun_it) {
+                       struct fsg_lun *curlun = *curlun_it;
+                       if (!curlun || !fsg_lun_is_open(curlun))
                                continue;
 
                        fsg_lun_close(curlun);
@@ -2580,6 +2573,56 @@ static int fsg_main_thread(void *common_)
 
 /*************************** DEVICE ATTRIBUTES ***************************/
 
+static ssize_t ro_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+
+       return fsg_show_ro(curlun, buf);
+}
+
+static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+
+       return fsg_show_nofua(curlun, buf);
+}
+
+static ssize_t file_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
+
+       return fsg_show_file(curlun, filesem, buf);
+}
+
+static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
+
+       return fsg_store_ro(curlun, filesem, buf, count);
+}
+
+static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+
+       return fsg_store_nofua(curlun, buf, count);
+}
+
+static ssize_t file_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
+
+       return fsg_store_file(curlun, filesem, buf, count);
+}
+
 static DEVICE_ATTR_RW(ro);
 static DEVICE_ATTR_RW(nofua);
 static DEVICE_ATTR_RW(file);
@@ -2597,221 +2640,422 @@ static void fsg_lun_release(struct device *dev)
        /* Nothing needs to be done */
 }
 
-static inline void fsg_common_get(struct fsg_common *common)
+void fsg_common_get(struct fsg_common *common)
 {
        kref_get(&common->ref);
 }
+EXPORT_SYMBOL_GPL(fsg_common_get);
 
-static inline void fsg_common_put(struct fsg_common *common)
+void fsg_common_put(struct fsg_common *common)
 {
        kref_put(&common->ref, fsg_common_release);
 }
+EXPORT_SYMBOL_GPL(fsg_common_put);
 
-static struct fsg_common *fsg_common_init(struct fsg_common *common,
-                                         struct usb_composite_dev *cdev,
-                                         struct fsg_config *cfg)
+/* check if fsg_num_buffers is within a valid range */
+static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers)
 {
-       struct usb_gadget *gadget = cdev->gadget;
-       struct fsg_buffhd *bh;
-       struct fsg_lun *curlun;
-       struct fsg_lun_config *lcfg;
-       int nluns, i, rc;
-       char *pathbuf;
-
-       rc = fsg_num_buffers_validate();
-       if (rc != 0)
-               return ERR_PTR(rc);
-
-       /* Find out how many LUNs there should be */
-       nluns = cfg->nluns;
-       if (nluns < 1 || nluns > FSG_MAX_LUNS) {
-               dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns);
-               return ERR_PTR(-EINVAL);
-       }
+       if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
+               return 0;
+       pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
+              fsg_num_buffers, 2, 4);
+       return -EINVAL;
+}
 
-       /* Allocate? */
+static struct fsg_common *fsg_common_setup(struct fsg_common *common)
+{
        if (!common) {
-               common = kzalloc(sizeof *common, GFP_KERNEL);
+               common = kzalloc(sizeof(*common), GFP_KERNEL);
                if (!common)
                        return ERR_PTR(-ENOMEM);
                common->free_storage_on_release = 1;
        } else {
-               memset(common, 0, sizeof *common);
                common->free_storage_on_release = 0;
        }
+       init_rwsem(&common->filesem);
+       spin_lock_init(&common->lock);
+       kref_init(&common->ref);
+       init_completion(&common->thread_notifier);
+       init_waitqueue_head(&common->fsg_wait);
+       common->state = FSG_STATE_TERMINATED;
 
-       common->buffhds = kcalloc(fsg_num_buffers,
-                                 sizeof *(common->buffhds), GFP_KERNEL);
-       if (!common->buffhds) {
-               if (common->free_storage_on_release)
-                       kfree(common);
-               return ERR_PTR(-ENOMEM);
+       return common;
+}
+
+void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs)
+{
+       common->sysfs = sysfs;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_sysfs);
+
+static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n)
+{
+       if (buffhds) {
+               struct fsg_buffhd *bh = buffhds;
+               while (n--) {
+                       kfree(bh->buf);
+                       ++bh;
+               }
+               kfree(buffhds);
        }
+}
 
-       common->ops = cfg->ops;
-       common->private_data = cfg->private_data;
+int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n)
+{
+       struct fsg_buffhd *bh, *buffhds;
+       int i, rc;
 
-       common->gadget = gadget;
-       common->ep0 = gadget->ep0;
-       common->ep0req = cdev->req;
-       common->cdev = cdev;
+       rc = fsg_num_buffers_validate(n);
+       if (rc != 0)
+               return rc;
+
+       buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL);
+       if (!buffhds)
+               return -ENOMEM;
 
-       /* Maybe allocate device-global string IDs, and patch descriptors */
-       if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
-               rc = usb_string_id(cdev);
-               if (unlikely(rc < 0))
+       /* Data buffers cyclic list */
+       bh = buffhds;
+       i = n;
+       goto buffhds_first_it;
+       do {
+               bh->next = bh + 1;
+               ++bh;
+buffhds_first_it:
+               bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
+               if (unlikely(!bh->buf))
                        goto error_release;
-               fsg_strings[FSG_STRING_INTERFACE].id = rc;
-               fsg_intf_desc.iInterface = rc;
-       }
+       } while (--i);
+       bh->next = buffhds;
 
+       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
+       common->fsg_num_buffers = n;
+       common->buffhds = buffhds;
+
+       return 0;
+
+error_release:
        /*
-        * Create the LUNs, open their backing files, and register the
-        * LUN devices in sysfs.
+        * "buf"s pointed to by heads after n - i are NULL
+        * so releasing them won't hurt
         */
-       curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
-       if (unlikely(!curlun)) {
-               rc = -ENOMEM;
-               goto error_release;
+       _fsg_common_free_buffers(buffhds, n);
+
+       return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers);
+
+static inline void fsg_common_remove_sysfs(struct fsg_lun *lun)
+{
+       device_remove_file(&lun->dev, &dev_attr_nofua);
+       /*
+        * device_remove_file() =>
+        *
+        * here the attr (e.g. dev_attr_ro) is only used to be passed to:
+        *
+        *      sysfs_remove_file() =>
+        *
+        *      here e.g. both dev_attr_ro_cdrom and dev_attr_ro are in
+        *      the same namespace and
+        *      from here only attr->name is passed to:
+        *
+        *              sysfs_hash_and_remove()
+        *
+        *              attr->name is the same for dev_attr_ro_cdrom and
+        *              dev_attr_ro
+        *              attr->name is the same for dev_attr_file and
+        *              dev_attr_file_nonremovable
+        *
+        * so we don't differentiate between removing e.g. dev_attr_ro_cdrom
+        * and dev_attr_ro
+        */
+       device_remove_file(&lun->dev, &dev_attr_ro);
+       device_remove_file(&lun->dev, &dev_attr_file);
+}
+
+void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs)
+{
+       if (sysfs) {
+               fsg_common_remove_sysfs(lun);
+               device_unregister(&lun->dev);
        }
-       common->luns = curlun;
+       fsg_lun_close(lun);
+       kfree(lun);
+}
+EXPORT_SYMBOL_GPL(fsg_common_remove_lun);
 
-       init_rwsem(&common->filesem);
+static void _fsg_common_remove_luns(struct fsg_common *common, int n)
+{
+       int i;
 
-       for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
-               curlun->cdrom = !!lcfg->cdrom;
-               curlun->ro = lcfg->cdrom || lcfg->ro;
-               curlun->initially_ro = curlun->ro;
-               curlun->removable = lcfg->removable;
-               curlun->dev.release = fsg_lun_release;
-               curlun->dev.parent = &gadget->dev;
-               /* curlun->dev.driver = &fsg_driver.driver; XXX */
-               dev_set_drvdata(&curlun->dev, &common->filesem);
-               dev_set_name(&curlun->dev, "lun%d", i);
-
-               rc = device_register(&curlun->dev);
-               if (rc) {
-                       INFO(common, "failed to register LUN%d: %d\n", i, rc);
-                       common->nluns = i;
-                       put_device(&curlun->dev);
-                       goto error_release;
+       for (i = 0; i < n; ++i)
+               if (common->luns[i]) {
+                       fsg_common_remove_lun(common->luns[i], common->sysfs);
+                       common->luns[i] = NULL;
                }
+}
+EXPORT_SYMBOL_GPL(fsg_common_remove_luns);
 
-               rc = device_create_file(&curlun->dev,
-                                       curlun->cdrom
-                                     ? &dev_attr_ro_cdrom
-                                     : &dev_attr_ro);
-               if (rc)
-                       goto error_luns;
-               rc = device_create_file(&curlun->dev,
-                                       curlun->removable
-                                     ? &dev_attr_file
-                                     : &dev_attr_file_nonremovable);
-               if (rc)
-                       goto error_luns;
-               rc = device_create_file(&curlun->dev, &dev_attr_nofua);
-               if (rc)
-                       goto error_luns;
+void fsg_common_remove_luns(struct fsg_common *common)
+{
+       _fsg_common_remove_luns(common, common->nluns);
+}
 
-               if (lcfg->filename) {
-                       rc = fsg_lun_open(curlun, lcfg->filename);
-                       if (rc)
-                               goto error_luns;
-               } else if (!curlun->removable) {
-                       ERROR(common, "no file given for LUN%d\n", i);
-                       rc = -EINVAL;
-                       goto error_luns;
-               }
+void fsg_common_free_luns(struct fsg_common *common)
+{
+       fsg_common_remove_luns(common);
+       kfree(common->luns);
+       common->luns = NULL;
+}
+EXPORT_SYMBOL_GPL(fsg_common_free_luns);
+
+int fsg_common_set_nluns(struct fsg_common *common, int nluns)
+{
+       struct fsg_lun **curlun;
+
+       /* Find out how many LUNs there should be */
+       if (nluns < 1 || nluns > FSG_MAX_LUNS) {
+               pr_err("invalid number of LUNs: %u\n", nluns);
+               return -EINVAL;
        }
+
+       curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
+       if (unlikely(!curlun))
+               return -ENOMEM;
+
+       if (common->luns)
+               fsg_common_free_luns(common);
+
+       common->luns = curlun;
        common->nluns = nluns;
 
-       /* Data buffers cyclic list */
-       bh = common->buffhds;
-       i = fsg_num_buffers;
-       goto buffhds_first_it;
-       do {
-               bh->next = bh + 1;
-               ++bh;
-buffhds_first_it:
-               bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
-               if (unlikely(!bh->buf)) {
-                       rc = -ENOMEM;
-                       goto error_release;
-               }
-       } while (--i);
-       bh->next = common->buffhds;
+       pr_info("Number of LUNs=%d\n", common->nluns);
 
-       /* Prepare inquiryString */
-       i = get_default_bcdDevice();
-       snprintf(common->inquiry_string, sizeof common->inquiry_string,
-                "%-8s%-16s%04x", cfg->vendor_name ?: "Linux",
-                /* Assume product name dependent on the first LUN */
-                cfg->product_name ?: (common->luns->cdrom
-                                    ? "File-CD Gadget"
-                                    : "File-Stor Gadget"),
-                i);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_nluns);
+
+void fsg_common_set_ops(struct fsg_common *common,
+                       const struct fsg_operations *ops)
+{
+       common->ops = ops;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_ops);
+
+void fsg_common_free_buffers(struct fsg_common *common)
+{
+       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
+       common->buffhds = NULL;
+}
+EXPORT_SYMBOL_GPL(fsg_common_free_buffers);
+
+int fsg_common_set_cdev(struct fsg_common *common,
+                        struct usb_composite_dev *cdev, bool can_stall)
+{
+       struct usb_string *us;
+
+       common->gadget = cdev->gadget;
+       common->ep0 = cdev->gadget->ep0;
+       common->ep0req = cdev->req;
+       common->cdev = cdev;
+
+       us = usb_gstrings_attach(cdev, fsg_strings_array,
+                                ARRAY_SIZE(fsg_strings));
+       if (IS_ERR(us))
+               return PTR_ERR(us);
+
+       fsg_intf_desc.iInterface = us[FSG_STRING_INTERFACE].id;
 
        /*
         * Some peripheral controllers are known not to be able to
         * halt bulk endpoints correctly.  If one of them is present,
         * disable stalls.
         */
-       common->can_stall = cfg->can_stall &&
-               !(gadget_is_at91(common->gadget));
+       common->can_stall = can_stall && !(gadget_is_at91(common->gadget));
 
-       spin_lock_init(&common->lock);
-       kref_init(&common->ref);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_cdev);
 
-       /* Tell the thread to start working */
-       common->thread_task =
-               kthread_create(fsg_main_thread, common, "file-storage");
-       if (IS_ERR(common->thread_task)) {
-               rc = PTR_ERR(common->thread_task);
-               goto error_release;
+static inline int fsg_common_add_sysfs(struct fsg_common *common,
+                                      struct fsg_lun *lun)
+{
+       int rc;
+
+       rc = device_register(&lun->dev);
+       if (rc) {
+               put_device(&lun->dev);
+               return rc;
        }
-       init_completion(&common->thread_notifier);
-       init_waitqueue_head(&common->fsg_wait);
 
-       /* Information */
-       INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
-       INFO(common, "Number of LUNs=%d\n", common->nluns);
+       rc = device_create_file(&lun->dev,
+                               lun->cdrom
+                             ? &dev_attr_ro_cdrom
+                             : &dev_attr_ro);
+       if (rc)
+               goto error;
+       rc = device_create_file(&lun->dev,
+                               lun->removable
+                             ? &dev_attr_file
+                             : &dev_attr_file_nonremovable);
+       if (rc)
+               goto error;
+       rc = device_create_file(&lun->dev, &dev_attr_nofua);
+       if (rc)
+               goto error;
+
+       return 0;
+
+error:
+       /* removing nonexistent files is a no-op */
+       fsg_common_remove_sysfs(lun);
+       device_unregister(&lun->dev);
+       return rc;
+}
+
+int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
+                         unsigned int id, const char *name,
+                         const char **name_pfx)
+{
+       struct fsg_lun *lun;
+       char *pathbuf, *p;
+       int rc = -ENOMEM;
+
+       if (!common->nluns || !common->luns)
+               return -ENODEV;
+
+       if (common->luns[id])
+               return -EBUSY;
+
+       if (!cfg->filename && !cfg->removable) {
+               pr_err("no file given for LUN%d\n", id);
+               return -EINVAL;
+       }
+
+       lun = kzalloc(sizeof(*lun), GFP_KERNEL);
+       if (!lun)
+               return -ENOMEM;
+
+       lun->name_pfx = name_pfx;
+
+       lun->cdrom = !!cfg->cdrom;
+       lun->ro = cfg->cdrom || cfg->ro;
+       lun->initially_ro = lun->ro;
+       lun->removable = !!cfg->removable;
+
+       if (!common->sysfs) {
+               /* we DON'T own the name!*/
+               lun->name = name;
+       } else {
+               lun->dev.release = fsg_lun_release;
+               lun->dev.parent = &common->gadget->dev;
+               dev_set_drvdata(&lun->dev, &common->filesem);
+               dev_set_name(&lun->dev, "%s", name);
+               lun->name = dev_name(&lun->dev);
+
+               rc = fsg_common_add_sysfs(common, lun);
+               if (rc) {
+                       pr_info("failed to register LUN%d: %d\n", id, rc);
+                       goto error_sysfs;
+               }
+       }
+
+       common->luns[id] = lun;
+
+       if (cfg->filename) {
+               rc = fsg_lun_open(lun, cfg->filename);
+               if (rc)
+                       goto error_lun;
+       }
 
        pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
-       for (i = 0, nluns = common->nluns, curlun = common->luns;
-            i < nluns;
-            ++curlun, ++i) {
-               char *p = "(no medium)";
-               if (fsg_lun_is_open(curlun)) {
-                       p = "(error)";
-                       if (pathbuf) {
-                               p = d_path(&curlun->filp->f_path,
-                                          pathbuf, PATH_MAX);
-                               if (IS_ERR(p))
-                                       p = "(error)";
-                       }
+       p = "(no medium)";
+       if (fsg_lun_is_open(lun)) {
+               p = "(error)";
+               if (pathbuf) {
+                       p = d_path(&lun->filp->f_path, pathbuf, PATH_MAX);
+                       if (IS_ERR(p))
+                               p = "(error)";
                }
-               LINFO(curlun, "LUN: %s%s%sfile: %s\n",
-                     curlun->removable ? "removable " : "",
-                     curlun->ro ? "read only " : "",
-                     curlun->cdrom ? "CD-ROM " : "",
-                     p);
        }
+       pr_info("LUN: %s%s%sfile: %s\n",
+             lun->removable ? "removable " : "",
+             lun->ro ? "read only " : "",
+             lun->cdrom ? "CD-ROM " : "",
+             p);
        kfree(pathbuf);
 
+       return 0;
+
+error_lun:
+       if (common->sysfs) {
+               fsg_common_remove_sysfs(lun);
+               device_unregister(&lun->dev);
+       }
+       fsg_lun_close(lun);
+       common->luns[id] = NULL;
+error_sysfs:
+       kfree(lun);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_common_create_lun);
+
+int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg)
+{
+       char buf[8]; /* enough for 100000000 different numbers, decimal */
+       int i, rc;
+
+       for (i = 0; i < common->nluns; ++i) {
+               snprintf(buf, sizeof(buf), "lun%d", i);
+               rc = fsg_common_create_lun(common, &cfg->luns[i], i, buf, NULL);
+               if (rc)
+                       goto fail;
+       }
+
+       pr_info("Number of LUNs=%d\n", common->nluns);
+
+       return 0;
+
+fail:
+       _fsg_common_remove_luns(common, i);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_common_create_luns);
+
+void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
+                                  const char *pn)
+{
+       int i;
+
+       /* Prepare inquiryString */
+       i = get_default_bcdDevice();
+       snprintf(common->inquiry_string, sizeof(common->inquiry_string),
+                "%-8s%-16s%04x", vn ?: "Linux",
+                /* Assume product name dependent on the first LUN */
+                pn ?: ((*common->luns)->cdrom
+                    ? "File-CD Gadget"
+                    : "File-Stor Gadget"),
+                i);
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
+
+int fsg_common_run_thread(struct fsg_common *common)
+{
+       common->state = FSG_STATE_IDLE;
+       /* Tell the thread to start working */
+       common->thread_task =
+               kthread_create(fsg_main_thread, common, "file-storage");
+       if (IS_ERR(common->thread_task)) {
+               common->state = FSG_STATE_TERMINATED;
+               return PTR_ERR(common->thread_task);
+       }
+
        DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
 
        wake_up_process(common->thread_task);
 
-       return common;
-
-error_luns:
-       common->nluns = i + 1;
-error_release:
-       common->state = FSG_STATE_TERMINATED;   /* The thread is dead */
-       /* Call fsg_common_release() directly, ref might be not initialised. */
-       fsg_common_release(&common->ref);
-       return ERR_PTR(rc);
+       return 0;
 }
+EXPORT_SYMBOL_GPL(fsg_common_run_thread);
 
 static void fsg_common_release(struct kref *ref)
 {
@@ -2824,36 +3068,26 @@ static void fsg_common_release(struct kref *ref)
        }
 
        if (likely(common->luns)) {
-               struct fsg_lun *lun = common->luns;
+               struct fsg_lun **lun_it = common->luns;
                unsigned i = common->nluns;
 
                /* In error recovery common->nluns may be zero. */
-               for (; i; --i, ++lun) {
-                       device_remove_file(&lun->dev, &dev_attr_nofua);
-                       device_remove_file(&lun->dev,
-                                          lun->cdrom
-                                        ? &dev_attr_ro_cdrom
-                                        : &dev_attr_ro);
-                       device_remove_file(&lun->dev,
-                                          lun->removable
-                                        ? &dev_attr_file
-                                        : &dev_attr_file_nonremovable);
+               for (; i; --i, ++lun_it) {
+                       struct fsg_lun *lun = *lun_it;
+                       if (!lun)
+                               continue;
+                       if (common->sysfs)
+                               fsg_common_remove_sysfs(lun);
                        fsg_lun_close(lun);
-                       device_unregister(&lun->dev);
+                       if (common->sysfs)
+                               device_unregister(&lun->dev);
+                       kfree(lun);
                }
 
                kfree(common->luns);
        }
 
-       {
-               struct fsg_buffhd *bh = common->buffhds;
-               unsigned i = fsg_num_buffers;
-               do {
-                       kfree(bh->buf);
-               } while (++bh, --i);
-       }
-
-       kfree(common->buffhds);
+       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
        if (common->free_storage_on_release)
                kfree(common);
 }
@@ -2861,24 +3095,6 @@ static void fsg_common_release(struct kref *ref)
 
 /*-------------------------------------------------------------------------*/
 
-static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct fsg_dev          *fsg = fsg_from_func(f);
-       struct fsg_common       *common = fsg->common;
-
-       DBG(fsg, "unbind\n");
-       if (fsg->common->fsg == fsg) {
-               fsg->common->new_fsg = NULL;
-               raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
-               /* FIXME: make interruptible or killable somehow? */
-               wait_event(common->fsg_wait, common->fsg != fsg);
-       }
-
-       fsg_common_put(common);
-       usb_free_all_descriptors(&fsg->function);
-       kfree(fsg);
-}
-
 static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
 {
        struct fsg_dev          *fsg = fsg_from_func(f);
@@ -2887,6 +3103,19 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
        struct usb_ep           *ep;
        unsigned                max_burst;
        int                     ret;
+       struct fsg_opts         *opts;
+
+       opts = fsg_opts_from_func_inst(f->fi);
+       if (!opts->no_configfs) {
+               ret = fsg_common_set_cdev(fsg->common, c->cdev,
+                                         fsg->common->can_stall);
+               if (ret)
+                       return ret;
+               fsg_common_set_inquiry_string(fsg->common, 0, 0);
+               ret = fsg_common_run_thread(fsg->common);
+               if (ret)
+                       return ret;
+       }
 
        fsg->gadget = gadget;
 
@@ -2939,95 +3168,472 @@ autoconf_fail:
        return -ENOTSUPP;
 }
 
-/****************************** ADD FUNCTION ******************************/
+/****************************** ALLOCATE FUNCTION *************************/
 
-static struct usb_gadget_strings *fsg_strings_array[] = {
-       &fsg_stringtab,
+static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct fsg_dev          *fsg = fsg_from_func(f);
+       struct fsg_common       *common = fsg->common;
+
+       DBG(fsg, "unbind\n");
+       if (fsg->common->fsg == fsg) {
+               fsg->common->new_fsg = NULL;
+               raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+               /* FIXME: make interruptible or killable somehow? */
+               wait_event(common->fsg_wait, common->fsg != fsg);
+       }
+
+       usb_free_all_descriptors(&fsg->function);
+}
+
+static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct fsg_lun_opts, group);
+}
+
+static inline struct fsg_opts *to_fsg_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct fsg_opts,
+                           func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(fsg_lun_opts);
+CONFIGFS_ATTR_OPS(fsg_lun_opts);
+
+static void fsg_lun_attr_release(struct config_item *item)
+{
+       struct fsg_lun_opts *lun_opts;
+
+       lun_opts = to_fsg_lun_opts(item);
+       kfree(lun_opts);
+}
+
+static struct configfs_item_operations fsg_lun_item_ops = {
+       .release                = fsg_lun_attr_release,
+       .show_attribute         = fsg_lun_opts_attr_show,
+       .store_attribute        = fsg_lun_opts_attr_store,
+};
+
+static ssize_t fsg_lun_opts_file_show(struct fsg_lun_opts *opts, char *page)
+{
+       struct fsg_opts *fsg_opts;
+
+       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+       return fsg_show_file(opts->lun, &fsg_opts->common->filesem, page);
+}
+
+static ssize_t fsg_lun_opts_file_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       struct fsg_opts *fsg_opts;
+
+       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+       return fsg_store_file(opts->lun, &fsg_opts->common->filesem, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_file =
+       __CONFIGFS_ATTR(file, S_IRUGO | S_IWUSR, fsg_lun_opts_file_show,
+                       fsg_lun_opts_file_store);
+
+static ssize_t fsg_lun_opts_ro_show(struct fsg_lun_opts *opts, char *page)
+{
+       return fsg_show_ro(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_ro_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       struct fsg_opts *fsg_opts;
+
+       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+       return fsg_store_ro(opts->lun, &fsg_opts->common->filesem, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_ro =
+       __CONFIGFS_ATTR(ro, S_IRUGO | S_IWUSR, fsg_lun_opts_ro_show,
+                       fsg_lun_opts_ro_store);
+
+static ssize_t fsg_lun_opts_removable_show(struct fsg_lun_opts *opts,
+                                          char *page)
+{
+       return fsg_show_removable(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_removable_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       return fsg_store_removable(opts->lun, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_removable =
+       __CONFIGFS_ATTR(removable, S_IRUGO | S_IWUSR,
+                       fsg_lun_opts_removable_show,
+                       fsg_lun_opts_removable_store);
+
+static ssize_t fsg_lun_opts_cdrom_show(struct fsg_lun_opts *opts, char *page)
+{
+       return fsg_show_cdrom(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_cdrom_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       struct fsg_opts *fsg_opts;
+
+       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+       return fsg_store_cdrom(opts->lun, &fsg_opts->common->filesem, page,
+                              len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_cdrom =
+       __CONFIGFS_ATTR(cdrom, S_IRUGO | S_IWUSR, fsg_lun_opts_cdrom_show,
+                       fsg_lun_opts_cdrom_store);
+
+static ssize_t fsg_lun_opts_nofua_show(struct fsg_lun_opts *opts, char *page)
+{
+       return fsg_show_nofua(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_nofua_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       return fsg_store_nofua(opts->lun, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_nofua =
+       __CONFIGFS_ATTR(nofua, S_IRUGO | S_IWUSR, fsg_lun_opts_nofua_show,
+                       fsg_lun_opts_nofua_store);
+
+static struct configfs_attribute *fsg_lun_attrs[] = {
+       &fsg_lun_opts_file.attr,
+       &fsg_lun_opts_ro.attr,
+       &fsg_lun_opts_removable.attr,
+       &fsg_lun_opts_cdrom.attr,
+       &fsg_lun_opts_nofua.attr,
        NULL,
 };
 
-static int fsg_bind_config(struct usb_composite_dev *cdev,
-                          struct usb_configuration *c,
-                          struct fsg_common *common)
+static struct config_item_type fsg_lun_type = {
+       .ct_item_ops    = &fsg_lun_item_ops,
+       .ct_attrs       = fsg_lun_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *fsg_lun_make(struct config_group *group,
+                                        const char *name)
 {
-       struct fsg_dev *fsg;
+       struct fsg_lun_opts *opts;
+       struct fsg_opts *fsg_opts;
+       struct fsg_lun_config config;
+       char *num_str;
+       u8 num;
+       int ret;
+
+       num_str = strchr(name, '.');
+       if (!num_str) {
+               pr_err("Unable to locate . in LUN.NUMBER\n");
+               return ERR_PTR(-EINVAL);
+       }
+       num_str++;
+
+       ret = kstrtou8(num_str, 0, &num);
+       if (ret)
+               return ERR_PTR(ret);
+
+       fsg_opts = to_fsg_opts(&group->cg_item);
+       if (num >= FSG_MAX_LUNS)
+               return ERR_PTR(-ERANGE);
+
+       mutex_lock(&fsg_opts->lock);
+       if (fsg_opts->refcnt || fsg_opts->common->luns[num]) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       memset(&config, 0, sizeof(config));
+       config.removable = true;
+
+       ret = fsg_common_create_lun(fsg_opts->common, &config, num, name,
+                                   (const char **)&group->cg_item.ci_name);
+       if (ret) {
+               kfree(opts);
+               goto out;
+       }
+       opts->lun = fsg_opts->common->luns[num];
+       opts->lun_id = num;
+       mutex_unlock(&fsg_opts->lock);
+
+       config_group_init_type_name(&opts->group, name, &fsg_lun_type);
+
+       return &opts->group;
+out:
+       mutex_unlock(&fsg_opts->lock);
+       return ERR_PTR(ret);
+}
+
+static void fsg_lun_drop(struct config_group *group, struct config_item *item)
+{
+       struct fsg_lun_opts *lun_opts;
+       struct fsg_opts *fsg_opts;
+
+       lun_opts = to_fsg_lun_opts(item);
+       fsg_opts = to_fsg_opts(&group->cg_item);
+
+       mutex_lock(&fsg_opts->lock);
+       if (fsg_opts->refcnt) {
+               struct config_item *gadget;
+
+               gadget = group->cg_item.ci_parent->ci_parent;
+               unregister_gadget_item(gadget);
+       }
+
+       fsg_common_remove_lun(lun_opts->lun, fsg_opts->common->sysfs);
+       fsg_opts->common->luns[lun_opts->lun_id] = NULL;
+       lun_opts->lun_id = 0;
+       mutex_unlock(&fsg_opts->lock);
+
+       config_item_put(item);
+}
+
+CONFIGFS_ATTR_STRUCT(fsg_opts);
+CONFIGFS_ATTR_OPS(fsg_opts);
+
+static void fsg_attr_release(struct config_item *item)
+{
+       struct fsg_opts *opts = to_fsg_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations fsg_item_ops = {
+       .release                = fsg_attr_release,
+       .show_attribute         = fsg_opts_attr_show,
+       .store_attribute        = fsg_opts_attr_store,
+};
+
+static ssize_t fsg_opts_stall_show(struct fsg_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->common->can_stall);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t fsg_opts_stall_store(struct fsg_opts *opts, const char *page,
+                                   size_t len)
+{
+       int ret;
+       bool stall;
+
+       mutex_lock(&opts->lock);
+
+       if (opts->refcnt) {
+               mutex_unlock(&opts->lock);
+               return -EBUSY;
+       }
+
+       ret = strtobool(page, &stall);
+       if (!ret) {
+               opts->common->can_stall = stall;
+               ret = len;
+       }
+
+       mutex_unlock(&opts->lock);
+
+       return ret;
+}
+
+static struct fsg_opts_attribute fsg_opts_stall =
+       __CONFIGFS_ATTR(stall, S_IRUGO | S_IWUSR, fsg_opts_stall_show,
+                       fsg_opts_stall_store);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+static ssize_t fsg_opts_num_buffers_show(struct fsg_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->common->fsg_num_buffers);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t fsg_opts_num_buffers_store(struct fsg_opts *opts,
+                                         const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       ret = fsg_num_buffers_validate(num);
+       if (ret)
+               goto end;
+
+       fsg_common_set_num_buffers(opts->common, num);
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct fsg_opts_attribute fsg_opts_num_buffers =
+       __CONFIGFS_ATTR(num_buffers, S_IRUGO | S_IWUSR,
+                       fsg_opts_num_buffers_show,
+                       fsg_opts_num_buffers_store);
+
+#endif
+
+static struct configfs_attribute *fsg_attrs[] = {
+       &fsg_opts_stall.attr,
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+       &fsg_opts_num_buffers.attr,
+#endif
+       NULL,
+};
+
+static struct configfs_group_operations fsg_group_ops = {
+       .make_group     = fsg_lun_make,
+       .drop_item      = fsg_lun_drop,
+};
+
+static struct config_item_type fsg_func_type = {
+       .ct_item_ops    = &fsg_item_ops,
+       .ct_group_ops   = &fsg_group_ops,
+       .ct_attrs       = fsg_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void fsg_free_inst(struct usb_function_instance *fi)
+{
+       struct fsg_opts *opts;
+
+       opts = fsg_opts_from_func_inst(fi);
+       fsg_common_put(opts->common);
+       kfree(opts);
+}
+
+static struct usb_function_instance *fsg_alloc_inst(void)
+{
+       struct fsg_opts *opts;
+       struct fsg_lun_config config;
        int rc;
 
-       fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = fsg_free_inst;
+       opts->common = fsg_common_setup(opts->common);
+       if (IS_ERR(opts->common)) {
+               rc = PTR_ERR(opts->common);
+               goto release_opts;
+       }
+       rc = fsg_common_set_nluns(opts->common, FSG_MAX_LUNS);
+       if (rc)
+               goto release_opts;
+
+       rc = fsg_common_set_num_buffers(opts->common,
+                                       CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
+       if (rc)
+               goto release_luns;
+
+       pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
+
+       memset(&config, 0, sizeof(config));
+       config.removable = true;
+       rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",
+                       (const char **)&opts->func_inst.group.cg_item.ci_name);
+       opts->lun0.lun = opts->common->luns[0];
+       opts->lun0.lun_id = 0;
+       config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);
+       opts->default_groups[0] = &opts->lun0.group;
+       opts->func_inst.group.default_groups = opts->default_groups;
+
+       config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type);
+
+       return &opts->func_inst;
+
+release_luns:
+       kfree(opts->common->luns);
+release_opts:
+       kfree(opts);
+       return ERR_PTR(rc);
+}
+
+static void fsg_free(struct usb_function *f)
+{
+       struct fsg_dev *fsg;
+       struct fsg_opts *opts;
+
+       fsg = container_of(f, struct fsg_dev, function);
+       opts = container_of(f->fi, struct fsg_opts, func_inst);
+
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+
+       kfree(fsg);
+}
+
+static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
+{
+       struct fsg_opts *opts = fsg_opts_from_func_inst(fi);
+       struct fsg_common *common = opts->common;
+       struct fsg_dev *fsg;
+
+       fsg = kzalloc(sizeof(*fsg), GFP_KERNEL);
        if (unlikely(!fsg))
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
-       fsg->function.name        = FSG_DRIVER_DESC;
-       fsg->function.strings     = fsg_strings_array;
-       fsg->function.bind        = fsg_bind;
-       fsg->function.unbind      = fsg_unbind;
-       fsg->function.setup       = fsg_setup;
-       fsg->function.set_alt     = fsg_set_alt;
-       fsg->function.disable     = fsg_disable;
+       mutex_lock(&opts->lock);
+       opts->refcnt++;
+       mutex_unlock(&opts->lock);
+       fsg->function.name      = FSG_DRIVER_DESC;
+       fsg->function.bind      = fsg_bind;
+       fsg->function.unbind    = fsg_unbind;
+       fsg->function.setup     = fsg_setup;
+       fsg->function.set_alt   = fsg_set_alt;
+       fsg->function.disable   = fsg_disable;
+       fsg->function.free_func = fsg_free;
 
        fsg->common               = common;
-       /*
-        * Our caller holds a reference to common structure so we
-        * don't have to be worry about it being freed until we return
-        * from this function.  So instead of incrementing counter now
-        * and decrement in error recovery we increment it only when
-        * call to usb_add_function() was successful.
-        */
 
-       rc = usb_add_function(c, &fsg->function);
-       if (unlikely(rc))
-               kfree(fsg);
-       else
-               fsg_common_get(fsg->common);
-       return rc;
+       return &fsg->function;
 }
 
+DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Nazarewicz");
 
 /************************* Module parameters *************************/
 
-struct fsg_module_parameters {
-       char            *file[FSG_MAX_LUNS];
-       bool            ro[FSG_MAX_LUNS];
-       bool            removable[FSG_MAX_LUNS];
-       bool            cdrom[FSG_MAX_LUNS];
-       bool            nofua[FSG_MAX_LUNS];
-
-       unsigned int    file_count, ro_count, removable_count, cdrom_count;
-       unsigned int    nofua_count;
-       unsigned int    luns;   /* nluns */
-       bool            stall;  /* can_stall */
-};
 
-#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc)      \
-       module_param_array_named(prefix ## name, params.name, type,     \
-                                &prefix ## params.name ## _count,      \
-                                S_IRUGO);                              \
-       MODULE_PARM_DESC(prefix ## name, desc)
-
-#define _FSG_MODULE_PARAM(prefix, params, name, type, desc)            \
-       module_param_named(prefix ## name, params.name, type,           \
-                          S_IRUGO);                                    \
-       MODULE_PARM_DESC(prefix ## name, desc)
-
-#define FSG_MODULE_PARAMETERS(prefix, params)                          \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp,            \
-                               "names of backing files or devices");   \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool,               \
-                               "true to force read-only");             \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool,        \
-                               "true to simulate removable media");    \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool,            \
-                               "true to simulate CD-ROM instead of disk"); \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool,            \
-                               "true to ignore SCSI WRITE(10,12) FUA bit"); \
-       _FSG_MODULE_PARAM(prefix, params, luns, uint,                   \
-                         "number of LUNs");                            \
-       _FSG_MODULE_PARAM(prefix, params, stall, bool,                  \
-                         "false to prevent bulk stalls")
-
-static void
-fsg_config_from_params(struct fsg_config *cfg,
-                      const struct fsg_module_parameters *params)
+void fsg_config_from_params(struct fsg_config *cfg,
+                      const struct fsg_module_parameters *params,
+                      unsigned int fsg_num_buffers)
 {
        struct fsg_lun_config *lun;
        unsigned i;
@@ -3055,19 +3661,7 @@ fsg_config_from_params(struct fsg_config *cfg,
 
        /* Finalise */
        cfg->can_stall = params->stall;
+       cfg->fsg_num_buffers = fsg_num_buffers;
 }
+EXPORT_SYMBOL_GPL(fsg_config_from_params);
 
-static inline struct fsg_common *
-fsg_common_from_params(struct fsg_common *common,
-                      struct usb_composite_dev *cdev,
-                      const struct fsg_module_parameters *params)
-       __attribute__((unused));
-static inline struct fsg_common *
-fsg_common_from_params(struct fsg_common *common,
-                      struct usb_composite_dev *cdev,
-                      const struct fsg_module_parameters *params)
-{
-       struct fsg_config cfg;
-       fsg_config_from_params(&cfg, params);
-       return fsg_common_init(common, cdev, &cfg);
-}
diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/f_mass_storage.h
new file mode 100644 (file)
index 0000000..b4866fc
--- /dev/null
@@ -0,0 +1,166 @@
+#ifndef USB_F_MASS_STORAGE_H
+#define USB_F_MASS_STORAGE_H
+
+#include <linux/usb/composite.h>
+#include "storage_common.h"
+
+struct fsg_module_parameters {
+       char            *file[FSG_MAX_LUNS];
+       bool            ro[FSG_MAX_LUNS];
+       bool            removable[FSG_MAX_LUNS];
+       bool            cdrom[FSG_MAX_LUNS];
+       bool            nofua[FSG_MAX_LUNS];
+
+       unsigned int    file_count, ro_count, removable_count, cdrom_count;
+       unsigned int    nofua_count;
+       unsigned int    luns;   /* nluns */
+       bool            stall;  /* can_stall */
+};
+
+#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc)      \
+       module_param_array_named(prefix ## name, params.name, type,     \
+                                &prefix ## params.name ## _count,      \
+                                S_IRUGO);                              \
+       MODULE_PARM_DESC(prefix ## name, desc)
+
+#define _FSG_MODULE_PARAM(prefix, params, name, type, desc)            \
+       module_param_named(prefix ## name, params.name, type,           \
+                          S_IRUGO);                                    \
+       MODULE_PARM_DESC(prefix ## name, desc)
+
+#define __FSG_MODULE_PARAMETERS(prefix, params)                                \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp,            \
+                               "names of backing files or devices");   \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool,               \
+                               "true to force read-only");             \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool,        \
+                               "true to simulate removable media");    \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool,            \
+                               "true to simulate CD-ROM instead of disk"); \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool,            \
+                               "true to ignore SCSI WRITE(10,12) FUA bit"); \
+       _FSG_MODULE_PARAM(prefix, params, luns, uint,                   \
+                         "number of LUNs");                            \
+       _FSG_MODULE_PARAM(prefix, params, stall, bool,                  \
+                         "false to prevent bulk stalls")
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#define FSG_MODULE_PARAMETERS(prefix, params)                          \
+       __FSG_MODULE_PARAMETERS(prefix, params);                        \
+       module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\
+       MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers")
+#else
+
+#define FSG_MODULE_PARAMETERS(prefix, params)                          \
+       __FSG_MODULE_PARAMETERS(prefix, params)
+
+#endif
+
+struct fsg_common;
+
+/* FSF callback functions */
+struct fsg_operations {
+       /*
+        * Callback function to call when thread exits.  If no
+        * callback is set or it returns value lower then zero MSF
+        * will force eject all LUNs it operates on (including those
+        * marked as non-removable or with prevent_medium_removal flag
+        * set).
+        */
+       int (*thread_exits)(struct fsg_common *common);
+};
+
+struct fsg_lun_opts {
+       struct config_group group;
+       struct fsg_lun *lun;
+       int lun_id;
+};
+
+struct fsg_opts {
+       struct fsg_common *common;
+       struct usb_function_instance func_inst;
+       struct fsg_lun_opts lun0;
+       struct config_group *default_groups[2];
+       bool no_configfs; /* for legacy gadgets */
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+struct fsg_lun_config {
+       const char *filename;
+       char ro;
+       char removable;
+       char cdrom;
+       char nofua;
+};
+
+struct fsg_config {
+       unsigned nluns;
+       struct fsg_lun_config luns[FSG_MAX_LUNS];
+
+       /* Callback functions. */
+       const struct fsg_operations     *ops;
+       /* Gadget's private data. */
+       void                    *private_data;
+
+       const char *vendor_name;                /*  8 characters or less */
+       const char *product_name;               /* 16 characters or less */
+
+       char                    can_stall;
+       unsigned int            fsg_num_buffers;
+};
+
+static inline struct fsg_opts *
+fsg_opts_from_func_inst(const struct usb_function_instance *fi)
+{
+       return container_of(fi, struct fsg_opts, func_inst);
+}
+
+void fsg_common_get(struct fsg_common *common);
+
+void fsg_common_put(struct fsg_common *common);
+
+void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs);
+
+int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n);
+
+void fsg_common_free_buffers(struct fsg_common *common);
+
+int fsg_common_set_cdev(struct fsg_common *common,
+                       struct usb_composite_dev *cdev, bool can_stall);
+
+void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs);
+
+void fsg_common_remove_luns(struct fsg_common *common);
+
+void fsg_common_free_luns(struct fsg_common *common);
+
+int fsg_common_set_nluns(struct fsg_common *common, int nluns);
+
+void fsg_common_set_ops(struct fsg_common *common,
+                       const struct fsg_operations *ops);
+
+int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
+                         unsigned int id, const char *name,
+                         const char **name_pfx);
+
+int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg);
+
+void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
+                                  const char *pn);
+
+int fsg_common_run_thread(struct fsg_common *common);
+
+void fsg_config_from_params(struct fsg_config *cfg,
+                           const struct fsg_module_parameters *params,
+                           unsigned int fsg_num_buffers);
+
+#endif /* USB_F_MASS_STORAGE_H */
index 5327c82472eda8034a54ca664392baf5875b0a7a..2344efe4f4ce8611e6869605e89e1c18aef7000a 100644 (file)
@@ -76,7 +76,9 @@ struct gfs_ffs_obj {
 
 USB_GADGET_COMPOSITE_OPTIONS();
 
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
 USB_ETHERNET_MODULE_PARAMETERS();
+#endif
 
 static struct usb_device_descriptor gfs_dev_desc = {
        .bLength                = sizeof gfs_dev_desc,
index c64deb9e3d62368e073893448dfe9f8b7ec610cc..f82768015715c69ed998e2cb95cc0735dd6a035b 100644 (file)
@@ -1165,7 +1165,7 @@ static int udc_proc_read(struct seq_file *m, void *v)
                                s = "invalid"; break;
                        default:
                                s = "?"; break;
-                       }; s; }),
+                       } s; }),
                        (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
                        (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
                        (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
@@ -1701,7 +1701,6 @@ static void goku_remove(struct pci_dev *pdev)
        if (dev->enabled)
                pci_disable_device(pdev);
 
-       pci_set_drvdata(pdev, NULL);
        dev->regs = NULL;
 
        INFO(dev, "unbind\n");
index 080e577773d527cb12507b9f20a581c45c452a42..8e27a8c9644470bfa8d5b44fc95a198aeda7214f 100644 (file)
 #define DRIVER_DESC            "Mass Storage Gadget"
 #define DRIVER_VERSION         "2009/09/11"
 
-/*-------------------------------------------------------------------------*/
-
 /*
- * kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module.  So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
  */
-#include "f_mass_storage.c"
+#define FSG_VENDOR_ID  0x0525  /* NetChip */
+#define FSG_PRODUCT_ID 0xa4a5  /* Linux-USB File-backed Storage Gadget */
+
+#include "f_mass_storage.h"
 
 /*-------------------------------------------------------------------------*/
 USB_GADGET_COMPOSITE_OPTIONS();
@@ -97,11 +97,28 @@ static struct usb_gadget_strings *dev_strings[] = {
        NULL,
 };
 
+static struct usb_function_instance *fi_msg;
+static struct usb_function *f_msg;
+
 /****************************** Configurations ******************************/
 
 static struct fsg_module_parameters mod_data = {
        .stall = 1
 };
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers        CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
 FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
 
 static unsigned long msg_registered;
@@ -115,13 +132,7 @@ static int msg_thread_exits(struct fsg_common *common)
 
 static int __init msg_do_config(struct usb_configuration *c)
 {
-       static const struct fsg_operations ops = {
-               .thread_exits = msg_thread_exits,
-       };
-       static struct fsg_common common;
-
-       struct fsg_common *retp;
-       struct fsg_config config;
+       struct fsg_opts *opts;
        int ret;
 
        if (gadget_is_otg(c->cdev->gadget)) {
@@ -129,15 +140,24 @@ static int __init msg_do_config(struct usb_configuration *c)
                c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
        }
 
-       fsg_config_from_params(&config, &mod_data);
-       config.ops = &ops;
+       opts = fsg_opts_from_func_inst(fi_msg);
+
+       f_msg = usb_get_function(fi_msg);
+       if (IS_ERR(f_msg))
+               return PTR_ERR(f_msg);
+
+       ret = fsg_common_run_thread(opts->common);
+       if (ret)
+               goto put_func;
+
+       ret = usb_add_function(c, f_msg);
+       if (ret)
+               goto put_func;
 
-       retp = fsg_common_init(&common, c->cdev, &config);
-       if (IS_ERR(retp))
-               return PTR_ERR(retp);
+       return 0;
 
-       ret = fsg_bind_config(c->cdev, c, &common);
-       fsg_common_put(&common);
+put_func:
+       usb_put_function(f_msg);
        return ret;
 }
 
@@ -152,23 +172,79 @@ static struct usb_configuration msg_config_driver = {
 
 static int __init msg_bind(struct usb_composite_dev *cdev)
 {
+       static const struct fsg_operations ops = {
+               .thread_exits = msg_thread_exits,
+       };
+       struct fsg_opts *opts;
+       struct fsg_config config;
        int status;
 
+       fi_msg = usb_get_function_instance("mass_storage");
+       if (IS_ERR(fi_msg))
+               return PTR_ERR(fi_msg);
+
+       fsg_config_from_params(&config, &mod_data, fsg_num_buffers);
+       opts = fsg_opts_from_func_inst(fi_msg);
+
+       opts->no_configfs = true;
+       status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
+       if (status)
+               goto fail;
+
+       status = fsg_common_set_nluns(opts->common, config.nluns);
+       if (status)
+               goto fail_set_nluns;
+
+       fsg_common_set_ops(opts->common, &ops);
+
+       status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
+       if (status)
+               goto fail_set_cdev;
+
+       fsg_common_set_sysfs(opts->common, true);
+       status = fsg_common_create_luns(opts->common, &config);
+       if (status)
+               goto fail_set_cdev;
+
+       fsg_common_set_inquiry_string(opts->common, config.vendor_name,
+                                     config.product_name);
+
        status = usb_string_ids_tab(cdev, strings_dev);
        if (status < 0)
-               return status;
+               goto fail_string_ids;
        msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
 
        status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
        if (status < 0)
-               return status;
+               goto fail_string_ids;
+
        usb_composite_overwrite_options(cdev, &coverwrite);
        dev_info(&cdev->gadget->dev,
                 DRIVER_DESC ", version: " DRIVER_VERSION "\n");
        set_bit(0, &msg_registered);
        return 0;
+
+fail_string_ids:
+       fsg_common_remove_luns(opts->common);
+fail_set_cdev:
+       fsg_common_free_luns(opts->common);
+fail_set_nluns:
+       fsg_common_free_buffers(opts->common);
+fail:
+       usb_put_function_instance(fi_msg);
+       return status;
 }
 
+static int msg_unbind(struct usb_composite_dev *cdev)
+{
+       if (!IS_ERR(f_msg))
+               usb_put_function(f_msg);
+
+       if (!IS_ERR(fi_msg))
+               usb_put_function_instance(fi_msg);
+
+       return 0;
+}
 
 /****************************** Some noise ******************************/
 
@@ -179,6 +255,7 @@ static __refdata struct usb_composite_driver msg_driver = {
        .needs_serial   = 1,
        .strings        = dev_strings,
        .bind           = msg_bind,
+       .unbind         = msg_unbind,
 };
 
 MODULE_DESCRIPTION(DRIVER_DESC);
index 23393254a8a35593526062cc09880f50c809dced..4fdaa54a2a2a395a2fa8804cdce9ffab4daa14c5 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/netdevice.h>
 
 #include "u_serial.h"
 #if defined USB_ETH_RNDIS
@@ -32,22 +33,11 @@ MODULE_AUTHOR("Michal Nazarewicz");
 MODULE_LICENSE("GPL");
 
 
-/***************************** All the files... *****************************/
+#include "f_mass_storage.h"
 
-/*
- * kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module.  So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "f_mass_storage.c"
-
-#define USBF_ECM_INCLUDED
-#include "f_ecm.c"
+#include "u_ecm.h"
 #ifdef USB_ETH_RNDIS
-#  define USB_FRNDIS_INCLUDED
-#  include "f_rndis.c"
+#  include "u_rndis.h"
 #  include "rndis.h"
 #endif
 #include "u_ether.h"
@@ -132,22 +122,36 @@ static struct usb_gadget_strings *dev_strings[] = {
 /****************************** Configurations ******************************/
 
 static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
-FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
 
-static struct fsg_common fsg_common;
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers        CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
 
-static u8 host_mac[ETH_ALEN];
+#endif /* CONFIG_USB_DEBUG */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
 
 static struct usb_function_instance *fi_acm;
-static struct eth_dev *the_dev;
+static struct usb_function_instance *fi_msg;
 
 /********** RNDIS **********/
 
 #ifdef USB_ETH_RNDIS
+static struct usb_function_instance *fi_rndis;
 static struct usb_function *f_acm_rndis;
+static struct usb_function *f_rndis;
+static struct usb_function *f_msg_rndis;
 
 static __init int rndis_do_config(struct usb_configuration *c)
 {
+       struct fsg_opts *fsg_opts;
        int ret;
 
        if (gadget_is_otg(c->cdev->gadget)) {
@@ -155,27 +159,50 @@ static __init int rndis_do_config(struct usb_configuration *c)
                c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
        }
 
-       ret = rndis_bind_config(c, host_mac, the_dev);
+       f_rndis = usb_get_function(fi_rndis);
+       if (IS_ERR(f_rndis))
+               return PTR_ERR(f_rndis);
+
+       ret = usb_add_function(c, f_rndis);
        if (ret < 0)
-               return ret;
+               goto err_func_rndis;
 
        f_acm_rndis = usb_get_function(fi_acm);
-       if (IS_ERR(f_acm_rndis))
-               return PTR_ERR(f_acm_rndis);
+       if (IS_ERR(f_acm_rndis)) {
+               ret = PTR_ERR(f_acm_rndis);
+               goto err_func_acm;
+       }
 
        ret = usb_add_function(c, f_acm_rndis);
        if (ret)
                goto err_conf;
 
-       ret = fsg_bind_config(c->cdev, c, &fsg_common);
-       if (ret < 0)
+       f_msg_rndis = usb_get_function(fi_msg);
+       if (IS_ERR(f_msg_rndis)) {
+               ret = PTR_ERR(f_msg_rndis);
                goto err_fsg;
+       }
+
+       fsg_opts = fsg_opts_from_func_inst(fi_msg);
+       ret = fsg_common_run_thread(fsg_opts->common);
+       if (ret)
+               goto err_run;
+
+       ret = usb_add_function(c, f_msg_rndis);
+       if (ret)
+               goto err_run;
 
        return 0;
+err_run:
+       usb_put_function(f_msg_rndis);
 err_fsg:
        usb_remove_function(c, f_acm_rndis);
 err_conf:
        usb_put_function(f_acm_rndis);
+err_func_acm:
+       usb_remove_function(c, f_rndis);
+err_func_rndis:
+       usb_put_function(f_rndis);
        return ret;
 }
 
@@ -205,10 +232,14 @@ static __ref int rndis_config_register(struct usb_composite_dev *cdev)
 /********** CDC ECM **********/
 
 #ifdef CONFIG_USB_G_MULTI_CDC
+static struct usb_function_instance *fi_ecm;
 static struct usb_function *f_acm_multi;
+static struct usb_function *f_ecm;
+static struct usb_function *f_msg_multi;
 
 static __init int cdc_do_config(struct usb_configuration *c)
 {
+       struct fsg_opts *fsg_opts;
        int ret;
 
        if (gadget_is_otg(c->cdev->gadget)) {
@@ -216,28 +247,51 @@ static __init int cdc_do_config(struct usb_configuration *c)
                c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
        }
 
-       ret = ecm_bind_config(c, host_mac, the_dev);
+       f_ecm = usb_get_function(fi_ecm);
+       if (IS_ERR(f_ecm))
+               return PTR_ERR(f_ecm);
+
+       ret = usb_add_function(c, f_ecm);
        if (ret < 0)
-               return ret;
+               goto err_func_ecm;
 
        /* implicit port_num is zero */
        f_acm_multi = usb_get_function(fi_acm);
-       if (IS_ERR(f_acm_multi))
-               return PTR_ERR(f_acm_multi);
+       if (IS_ERR(f_acm_multi)) {
+               ret = PTR_ERR(f_acm_multi);
+               goto err_func_acm;
+       }
 
        ret = usb_add_function(c, f_acm_multi);
        if (ret)
                goto err_conf;
 
-       ret = fsg_bind_config(c->cdev, c, &fsg_common);
-       if (ret < 0)
+       f_msg_multi = usb_get_function(fi_msg);
+       if (IS_ERR(f_msg_multi)) {
+               ret = PTR_ERR(f_msg_multi);
                goto err_fsg;
+       }
+
+       fsg_opts = fsg_opts_from_func_inst(fi_msg);
+       ret = fsg_common_run_thread(fsg_opts->common);
+       if (ret)
+               goto err_run;
+
+       ret = usb_add_function(c, f_msg_multi);
+       if (ret)
+               goto err_run;
 
        return 0;
+err_run:
+       usb_put_function(f_msg_multi);
 err_fsg:
        usb_remove_function(c, f_acm_multi);
 err_conf:
        usb_put_function(f_acm_multi);
+err_func_acm:
+       usb_remove_function(c, f_ecm);
+err_func_ecm:
+       usb_put_function(f_ecm);
        return ret;
 }
 
@@ -270,19 +324,67 @@ static __ref int cdc_config_register(struct usb_composite_dev *cdev)
 static int __ref multi_bind(struct usb_composite_dev *cdev)
 {
        struct usb_gadget *gadget = cdev->gadget;
+#ifdef CONFIG_USB_G_MULTI_CDC
+       struct f_ecm_opts *ecm_opts;
+#endif
+#ifdef USB_ETH_RNDIS
+       struct f_rndis_opts *rndis_opts;
+#endif
+       struct fsg_opts *fsg_opts;
+       struct fsg_config config;
        int status;
 
        if (!can_support_ecm(cdev->gadget)) {
                dev_err(&gadget->dev, "controller '%s' not usable\n",
-                       gadget->name);
+                       gadget->name);
                return -EINVAL;
        }
 
-       /* set up network link layer */
-       the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac,
-                              qmult);
-       if (IS_ERR(the_dev))
-               return PTR_ERR(the_dev);
+#ifdef CONFIG_USB_G_MULTI_CDC
+       fi_ecm = usb_get_function_instance("ecm");
+       if (IS_ERR(fi_ecm))
+               return PTR_ERR(fi_ecm);
+
+       ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+       gether_set_qmult(ecm_opts->net, qmult);
+       if (!gether_set_host_addr(ecm_opts->net, host_addr))
+               pr_info("using host ethernet address: %s", host_addr);
+       if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
+               pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#ifdef USB_ETH_RNDIS
+       fi_rndis = usb_get_function_instance("rndis");
+       if (IS_ERR(fi_rndis)) {
+               status = PTR_ERR(fi_rndis);
+               goto fail;
+       }
+
+       rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst);
+
+       gether_set_qmult(rndis_opts->net, qmult);
+       if (!gether_set_host_addr(rndis_opts->net, host_addr))
+               pr_info("using host ethernet address: %s", host_addr);
+       if (!gether_set_dev_addr(rndis_opts->net, dev_addr))
+               pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS)
+       /*
+        * If both ecm and rndis are selected then:
+        *      1) rndis borrows the net interface from ecm
+        *      2) since the interface is shared it must not be bound
+        *      twice - in ecm's _and_ rndis' binds, so do it here.
+        */
+       gether_set_gadget(ecm_opts->net, cdev->gadget);
+       status = gether_register_netdev(ecm_opts->net);
+       if (status)
+               goto fail0;
+
+       rndis_borrow_net(fi_rndis, ecm_opts->net);
+       ecm_opts->bound = true;
+#endif
 
        /* set up serial link layer */
        fi_acm = usb_get_function_instance("acm");
@@ -292,49 +394,87 @@ static int __ref multi_bind(struct usb_composite_dev *cdev)
        }
 
        /* set up mass storage function */
-       {
-               void *retp;
-               retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
-               if (IS_ERR(retp)) {
-                       status = PTR_ERR(retp);
-                       goto fail1;
-               }
+       fi_msg = usb_get_function_instance("mass_storage");
+       if (IS_ERR(fi_msg)) {
+               status = PTR_ERR(fi_msg);
+               goto fail1;
        }
+       fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
+       fsg_opts = fsg_opts_from_func_inst(fi_msg);
+
+       fsg_opts->no_configfs = true;
+       status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers);
+       if (status)
+               goto fail2;
+
+       status = fsg_common_set_nluns(fsg_opts->common, config.nluns);
+       if (status)
+               goto fail_set_nluns;
+
+       status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall);
+       if (status)
+               goto fail_set_cdev;
+
+       fsg_common_set_sysfs(fsg_opts->common, true);
+       status = fsg_common_create_luns(fsg_opts->common, &config);
+       if (status)
+               goto fail_set_cdev;
+
+       fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name,
+                                     config.product_name);
 
        /* allocate string IDs */
        status = usb_string_ids_tab(cdev, strings_dev);
        if (unlikely(status < 0))
-               goto fail2;
+               goto fail_string_ids;
        device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
 
        /* register configurations */
        status = rndis_config_register(cdev);
        if (unlikely(status < 0))
-               goto fail2;
+               goto fail_string_ids;
 
        status = cdc_config_register(cdev);
        if (unlikely(status < 0))
-               goto fail2;
+               goto fail_string_ids;
        usb_composite_overwrite_options(cdev, &coverwrite);
 
        /* we're done */
        dev_info(&gadget->dev, DRIVER_DESC "\n");
-       fsg_common_put(&fsg_common);
        return 0;
 
 
        /* error recovery */
+fail_string_ids:
+       fsg_common_remove_luns(fsg_opts->common);
+fail_set_cdev:
+       fsg_common_free_luns(fsg_opts->common);
+fail_set_nluns:
+       fsg_common_free_buffers(fsg_opts->common);
 fail2:
-       fsg_common_put(&fsg_common);
+       usb_put_function_instance(fi_msg);
 fail1:
        usb_put_function_instance(fi_acm);
 fail0:
-       gether_cleanup(the_dev);
+#ifdef USB_ETH_RNDIS
+       usb_put_function_instance(fi_rndis);
+fail:
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+       usb_put_function_instance(fi_ecm);
+#endif
        return status;
 }
 
 static int __exit multi_unbind(struct usb_composite_dev *cdev)
 {
+#ifdef CONFIG_USB_G_MULTI_CDC
+       usb_put_function(f_msg_multi);
+#endif
+#ifdef USB_ETH_RNDIS
+       usb_put_function(f_msg_rndis);
+#endif
+       usb_put_function_instance(fi_msg);
 #ifdef CONFIG_USB_G_MULTI_CDC
        usb_put_function(f_acm_multi);
 #endif
@@ -342,7 +482,14 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev)
        usb_put_function(f_acm_rndis);
 #endif
        usb_put_function_instance(fi_acm);
-       gether_cleanup(the_dev);
+#ifdef USB_ETH_RNDIS
+       usb_put_function(f_rndis);
+       usb_put_function_instance(fi_rndis);
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+       usb_put_function(f_ecm);
+       usb_put_function_instance(fi_ecm);
+#endif
        return 0;
 }
 
index 561b30efb8ee84de6fc37c33005cdadc6fbcc5ff..234711eabea14be4259eee06e8bf08ef07588b3a 100644 (file)
@@ -310,6 +310,7 @@ static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req,
         */
        trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma);
        if (!trb_hw) {
+               kfree(trb);
                dev_err(u3d->dev,
                        "%s, dma_pool_alloc fail\n", __func__);
                return NULL;
@@ -454,6 +455,7 @@ static int mv_u3d_req_to_trb(struct mv_u3d_req *req)
 
                trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC);
                if (!trb_hw) {
+                       kfree(trb);
                        dev_err(u3d->dev,
                                        "%s, trb_hw alloc fail\n", __func__);
                        return -ENOMEM;
@@ -1936,7 +1938,7 @@ static int mv_u3d_probe(struct platform_device *dev)
        }
        u3d->irq = r->start;
        if (request_irq(u3d->irq, mv_u3d_irq,
-               IRQF_DISABLED | IRQF_SHARED, driver_name, u3d)) {
+               IRQF_SHARED, driver_name, u3d)) {
                u3d->irq = 0;
                dev_err(&dev->dev, "Request irq %d for u3d failed\n",
                        u3d->irq);
index 0781bff70015da3e259106a829d50ad1a507e346..fc852177c08719428d9e445546adb0f53f99a47b 100644 (file)
@@ -129,7 +129,7 @@ static char *type_string (u8 bmAttributes)
        case USB_ENDPOINT_XFER_BULK:    return "bulk";
        case USB_ENDPOINT_XFER_ISOC:    return "iso";
        case USB_ENDPOINT_XFER_INT:     return "intr";
-       };
+       }
        return "control";
 }
 #endif
@@ -1630,7 +1630,7 @@ static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
                                        val = "intr"; break;
                                 default:
                                        val = "iso"; break;
-                                }; val; }),
+                                } val; }),
                                usb_endpoint_maxp (d) & 0x1fff,
                                ep->dma ? "dma" : "pio", ep->fifo_size
                                );
@@ -2680,7 +2680,6 @@ static void net2280_remove (struct pci_dev *pdev)
        if (dev->enabled)
                pci_disable_device (pdev);
        device_remove_file (&pdev->dev, &dev_attr_registers);
-       pci_set_drvdata (pdev, NULL);
 
        INFO (dev, "unbind\n");
 }
index 24174e1d15642be63c652340c80e81dc61d1bb15..32d5e923750b017a479d394e51ba09c991c8ef4d 100644 (file)
@@ -3080,7 +3080,6 @@ static void pch_udc_remove(struct pci_dev *pdev)
        if (dev->active)
                pci_disable_device(pdev);
        kfree(dev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM
index 9575085ded8112e05eddda04a60d52231b1f0a02..a3ad732bc812254798a11f4664093008729ba567 100644 (file)
@@ -1068,7 +1068,7 @@ static int rndis_proc_show(struct seq_file *m, void *v)
                                s = "RNDIS_INITIALIZED"; break;
                         case RNDIS_DATA_INITIALIZED:
                                s = "RNDIS_DATA_INITIALIZED"; break;
-                       }; s; }),
+                       } s; }),
                         param->medium,
                         (param->media_state) ? 0 : param->speed*100,
                         (param->media_state) ? "disconnected" : "connected",
index a8a99e4748d522bded4aef1882a7beaafe45603b..9875d9c0823f7c554744a40acb0a70897dde0bc6 100644 (file)
@@ -83,9 +83,12 @@ struct s3c_hsotg_req;
  * @dir_in: Set to true if this endpoint is of the IN direction, which
  *         means that it is sending data to the Host.
  * @index: The index for the endpoint registers.
+ * @mc: Multi Count - number of transactions per microframe
+ * @interval - Interval for periodic endpoints
  * @name: The name array passed to the USB core.
  * @halted: Set if the endpoint has been halted.
  * @periodic: Set if this is a periodic ep, such as Interrupt
+ * @isochronous: Set if this is a isochronous ep
  * @sent_zlp: Set if we've sent a zero-length packet.
  * @total_data: The total number of data bytes done.
  * @fifo_size: The size of the FIFO (for periodic IN endpoints)
@@ -121,9 +124,12 @@ struct s3c_hsotg_ep {
 
        unsigned char           dir_in;
        unsigned char           index;
+       unsigned char           mc;
+       unsigned char           interval;
 
        unsigned int            halted:1;
        unsigned int            periodic:1;
+       unsigned int            isochronous:1;
        unsigned int            sent_zlp:1;
 
        char                    name[10];
@@ -468,6 +474,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
        void *data;
        int can_write;
        int pkt_round;
+       int max_transfer;
 
        to_write -= (buf_pos - hs_ep->last_load);
 
@@ -535,8 +542,10 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
                can_write *= 4; /* fifo size is in 32bit quantities. */
        }
 
-       dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
-                __func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket);
+       max_transfer = hs_ep->ep.maxpacket * hs_ep->mc;
+
+       dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n",
+                __func__, gnptxsts, can_write, to_write, max_transfer);
 
        /*
         * limit to 512 bytes of data, it seems at least on the non-periodic
@@ -551,19 +560,21 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
         * the transfer to return that it did not run out of fifo space
         * doing it.
         */
-       if (to_write > hs_ep->ep.maxpacket) {
-               to_write = hs_ep->ep.maxpacket;
+       if (to_write > max_transfer) {
+               to_write = max_transfer;
 
-               s3c_hsotg_en_gsint(hsotg,
-                                  periodic ? GINTSTS_PTxFEmp :
-                                  GINTSTS_NPTxFEmp);
+               /* it's needed only when we do not use dedicated fifos */
+               if (!hsotg->dedicated_fifos)
+                       s3c_hsotg_en_gsint(hsotg,
+                                          periodic ? GINTSTS_PTxFEmp :
+                                          GINTSTS_NPTxFEmp);
        }
 
        /* see if we can write data */
 
        if (to_write > can_write) {
                to_write = can_write;
-               pkt_round = to_write % hs_ep->ep.maxpacket;
+               pkt_round = to_write % max_transfer;
 
                /*
                 * Round the write down to an
@@ -581,9 +592,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
                 * is more room left.
                 */
 
-               s3c_hsotg_en_gsint(hsotg,
-                                  periodic ? GINTSTS_PTxFEmp :
-                                  GINTSTS_NPTxFEmp);
+               /* it's needed only when we do not use dedicated fifos */
+               if (!hsotg->dedicated_fifos)
+                       s3c_hsotg_en_gsint(hsotg,
+                                          periodic ? GINTSTS_PTxFEmp :
+                                          GINTSTS_NPTxFEmp);
        }
 
        dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n",
@@ -727,8 +740,16 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
        else
                packets = 1;    /* send one packet if length is zero. */
 
+       if (hs_ep->isochronous && length > (hs_ep->mc * hs_ep->ep.maxpacket)) {
+               dev_err(hsotg->dev, "req length > maxpacket*mc\n");
+               return;
+       }
+
        if (dir_in && index != 0)
-               epsize = DxEPTSIZ_MC(1);
+               if (hs_ep->isochronous)
+                       epsize = DxEPTSIZ_MC(packets);
+               else
+                       epsize = DxEPTSIZ_MC(1);
        else
                epsize = 0;
 
@@ -820,6 +841,9 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
 
        dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n",
                __func__, readl(hsotg->regs + epctrl_reg));
+
+       /* enable ep interrupts */
+       s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1);
 }
 
 /**
@@ -1091,6 +1115,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
        bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
        struct s3c_hsotg_ep *ep;
        int ret;
+       bool halted;
 
        dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
                __func__, set ? "SET" : "CLEAR");
@@ -1105,6 +1130,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
 
                switch (le16_to_cpu(ctrl->wValue)) {
                case USB_ENDPOINT_HALT:
+                       halted = ep->halted;
+
                        s3c_hsotg_ep_sethalt(&ep->ep, set);
 
                        ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
@@ -1114,7 +1141,12 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
                                return ret;
                        }
 
-                       if (!set) {
+                       /*
+                        * we have to complete all requests for ep if it was
+                        * halted, and the halt was cleared by CLEAR_FEATURE
+                        */
+
+                       if (!set && halted) {
                                /*
                                 * If we have request in progress,
                                 * then complete it
@@ -1147,6 +1179,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
        return 1;
 }
 
+static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
+
 /**
  * s3c_hsotg_process_control - process a control request
  * @hsotg: The device state
@@ -1246,11 +1280,15 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
                 * don't believe we need to anything more to get the EP
                 * to reply with a STALL packet
                 */
+
+                /*
+                 * complete won't be called, so we enqueue
+                 * setup request here
+                 */
+                s3c_hsotg_enqueue_setup(hsotg);
        }
 }
 
-static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
-
 /**
  * s3c_hsotg_complete_setup - completion of a setup transfer
  * @ep: The endpoint the request was on.
@@ -1698,6 +1736,7 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
        struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
        void __iomem *regs = hsotg->regs;
        u32 mpsval;
+       u32 mcval;
        u32 reg;
 
        if (ep == 0) {
@@ -1705,15 +1744,19 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
                mpsval = s3c_hsotg_ep0_mps(mps);
                if (mpsval > 3)
                        goto bad_mps;
+               hs_ep->ep.maxpacket = mps;
+               hs_ep->mc = 1;
        } else {
-               if (mps >= DxEPCTL_MPS_LIMIT+1)
+               mpsval = mps & DxEPCTL_MPS_MASK;
+               if (mpsval > 1024)
                        goto bad_mps;
-
-               mpsval = mps;
+               mcval = ((mps >> 11) & 0x3) + 1;
+               hs_ep->mc = mcval;
+               if (mcval > 3)
+                       goto bad_mps;
+               hs_ep->ep.maxpacket = mpsval;
        }
 
-       hs_ep->ep.maxpacket = mps;
-
        /*
         * update both the in and out endpoint controldir_ registers, even
         * if one of the directions may not be in use.
@@ -1782,8 +1825,16 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
 {
        struct s3c_hsotg_req *hs_req = hs_ep->req;
 
-       if (!hs_ep->dir_in || !hs_req)
+       if (!hs_ep->dir_in || !hs_req) {
+               /**
+                * if request is not enqueued, we disable interrupts
+                * for endpoints, excepting ep0
+                */
+               if (hs_ep->index != 0)
+                       s3c_hsotg_ctrl_epint(hsotg, hs_ep->index,
+                                            hs_ep->dir_in, 0);
                return 0;
+       }
 
        if (hs_req->req.actual < hs_req->req.length) {
                dev_dbg(hsotg->dev, "trying to write more for ep%d\n",
@@ -1887,8 +1938,10 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
        u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
        u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
        u32 ints;
+       u32 ctrl;
 
        ints = readl(hsotg->regs + epint_reg);
+       ctrl = readl(hsotg->regs + epctl_reg);
 
        /* Clear endpoint interrupts */
        writel(ints, hsotg->regs + epint_reg);
@@ -1897,6 +1950,14 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
                __func__, idx, dir_in ? "in" : "out", ints);
 
        if (ints & DxEPINT_XferCompl) {
+               if (hs_ep->isochronous && hs_ep->interval == 1) {
+                       if (ctrl & DxEPCTL_EOFrNum)
+                               ctrl |= DxEPCTL_SetEvenFr;
+                       else
+                               ctrl |= DxEPCTL_SetOddFr;
+                       writel(ctrl, hsotg->regs + epctl_reg);
+               }
+
                dev_dbg(hsotg->dev,
                        "%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n",
                        __func__, readl(hsotg->regs + epctl_reg),
@@ -1963,7 +2024,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
        if (ints & DxEPINT_Back2BackSetup)
                dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__);
 
-       if (dir_in) {
+       if (dir_in && !hs_ep->isochronous) {
                /* not sure if this is important, but we'll clear it anyway */
                if (ints & DIEPMSK_INTknTXFEmpMsk) {
                        dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n",
@@ -2092,12 +2153,14 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
 }
 
 #define call_gadget(_hs, _entry) \
+do { \
        if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
            (_hs)->driver && (_hs)->driver->_entry) { \
                spin_unlock(&_hs->lock); \
                (_hs)->driver->_entry(&(_hs)->gadget); \
                spin_lock(&_hs->lock); \
-               }
+       } \
+} while (0)
 
 /**
  * s3c_hsotg_disconnect - disconnect service
@@ -2241,15 +2304,19 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
                       GAHBCFG_HBstLen_Incr4,
                       hsotg->regs + GAHBCFG);
        else
-               writel(GAHBCFG_GlblIntrEn, hsotg->regs + GAHBCFG);
+               writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NPTxFEmpLvl |
+                                                   GAHBCFG_PTxFEmpLvl) : 0) |
+                      GAHBCFG_GlblIntrEn,
+                      hsotg->regs + GAHBCFG);
 
        /*
-        * Enabling INTknTXFEmpMsk here seems to be a big mistake, we end
-        * up being flooded with interrupts if the host is polling the
-        * endpoint to try and read data.
+        * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts
+        * when we have no data to transfer. Otherwise we get being flooded by
+        * interrupts.
         */
 
-       writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty : 0) |
+       writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty |
+              DIEPMSK_INTknTXFEmpMsk : 0) |
               DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk |
               DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk |
               DIEPMSK_INTknEPMisMsk,
@@ -2378,10 +2445,14 @@ irq_retry:
 
        if (gintsts & (GINTSTS_OEPInt | GINTSTS_IEPInt)) {
                u32 daint = readl(hsotg->regs + DAINT);
-               u32 daint_out = daint >> DAINT_OutEP_SHIFT;
-               u32 daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT);
+               u32 daintmsk = readl(hsotg->regs + DAINTMSK);
+               u32 daint_out, daint_in;
                int ep;
 
+               daint &= daintmsk;
+               daint_out = daint >> DAINT_OutEP_SHIFT;
+               daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT);
+
                dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
 
                for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) {
@@ -2577,16 +2648,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
        epctrl |= DxEPCTL_SNAK;
 
        /* update the endpoint state */
-       hs_ep->ep.maxpacket = mps;
+       s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps);
 
        /* default, set to non-periodic */
+       hs_ep->isochronous = 0;
        hs_ep->periodic = 0;
+       hs_ep->halted = 0;
+       hs_ep->interval = desc->bInterval;
+
+       if (hs_ep->interval > 1 && hs_ep->mc > 1)
+               dev_err(hsotg->dev, "MC > 1 when interval is not 1\n");
 
        switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
        case USB_ENDPOINT_XFER_ISOC:
-               dev_err(hsotg->dev, "no current ISOC support\n");
-               ret = -EINVAL;
-               goto out;
+               epctrl |= DxEPCTL_EPType_Iso;
+               epctrl |= DxEPCTL_SetEvenFr;
+               hs_ep->isochronous = 1;
+               if (dir_in)
+                       hs_ep->periodic = 1;
+               break;
 
        case USB_ENDPOINT_XFER_BULK:
                epctrl |= DxEPCTL_EPType_Bulk;
@@ -2634,7 +2714,6 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
        /* enable the endpoint interrupt */
        s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
 
-out:
        spin_unlock_irqrestore(&hsotg->lock, flags);
        return ret;
 }
@@ -2776,6 +2855,8 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 
        writel(epctl, hs->regs + epreg);
 
+       hs_ep->halted = value;
+
        return 0;
 }
 
@@ -2903,7 +2984,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
        int ret;
 
        if (!hsotg) {
-               printk(KERN_ERR "%s: called with no device\n", __func__);
+               pr_err("%s: called with no device\n", __func__);
                return -ENODEV;
        }
 
@@ -3066,7 +3147,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
 
        hs_ep->parent = hsotg;
        hs_ep->ep.name = hs_ep->name;
-       hs_ep->ep.maxpacket = epnum ? 512 : EP0_MPS_LIMIT;
+       hs_ep->ep.maxpacket = epnum ? 1024 : EP0_MPS_LIMIT;
        hs_ep->ep.ops = &s3c_hsotg_ep_ops;
 
        /*
@@ -3200,7 +3281,7 @@ static int state_show(struct seq_file *seq, void *v)
                   readl(regs + GNPTXSTS),
                   readl(regs + GRXSTSR));
 
-       seq_printf(seq, "\nEndpoint status:\n");
+       seq_puts(seq, "\nEndpoint status:\n");
 
        for (idx = 0; idx < 15; idx++) {
                u32 in, out;
@@ -3217,7 +3298,7 @@ static int state_show(struct seq_file *seq, void *v)
                seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
                           in, out);
 
-               seq_printf(seq, "\n");
+               seq_puts(seq, "\n");
        }
 
        return 0;
@@ -3251,7 +3332,7 @@ static int fifo_show(struct seq_file *seq, void *v)
        u32 val;
        int idx;
 
-       seq_printf(seq, "Non-periodic FIFOs:\n");
+       seq_puts(seq, "Non-periodic FIFOs:\n");
        seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ));
 
        val = readl(regs + GNPTXFSIZ);
@@ -3259,7 +3340,7 @@ static int fifo_show(struct seq_file *seq, void *v)
                   val >> GNPTXFSIZ_NPTxFDep_SHIFT,
                   val & GNPTXFSIZ_NPTxFStAddr_MASK);
 
-       seq_printf(seq, "\nPeriodic TXFIFOs:\n");
+       seq_puts(seq, "\nPeriodic TXFIFOs:\n");
 
        for (idx = 1; idx <= 15; idx++) {
                val = readl(regs + DPTXFSIZn(idx));
@@ -3330,7 +3411,7 @@ static int ep_show(struct seq_file *seq, void *v)
                   readl(regs + DIEPTSIZ(index)),
                   readl(regs + DOEPTSIZ(index)));
 
-       seq_printf(seq, "\n");
+       seq_puts(seq, "\n");
        seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
        seq_printf(seq, "total_data=%ld\n", ep->total_data);
 
@@ -3341,7 +3422,7 @@ static int ep_show(struct seq_file *seq, void *v)
 
        list_for_each_entry(req, &ep->queue, queue) {
                if (--show_limit < 0) {
-                       seq_printf(seq, "not showing more requests...\n");
+                       seq_puts(seq, "not showing more requests...\n");
                        break;
                }
 
index 08a1a3210a2117f43b29f899085a21c547a9e644..ec20a1f50c2d702e56f6e6a79f03485607a36189 100644 (file)
  * The valid range of num_buffers is: num >= 2 && num <= 4.
  */
 
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/usb/composite.h>
 
-#include <linux/usb/storage.h>
-#include <scsi/scsi.h>
-#include <asm/unaligned.h>
-
-
-/*
- * Thanks to NetChip Technologies for donating this product ID.
- *
- * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
- * Instead:  allocate your own, using normal USB-IF procedures.
- */
-#define FSG_VENDOR_ID  0x0525  /* NetChip */
-#define FSG_PRODUCT_ID 0xa4a5  /* Linux-USB File-backed Storage Gadget */
-
-
-/*-------------------------------------------------------------------------*/
-
-
-#ifndef DEBUG
-#undef VERBOSE_DEBUG
-#undef DUMP_MSGS
-#endif /* !DEBUG */
-
-#ifdef VERBOSE_DEBUG
-#define VLDBG  LDBG
-#else
-#define VLDBG(lun, fmt, args...) do { } while (0)
-#endif /* VERBOSE_DEBUG */
-
-#define LDBG(lun, fmt, args...)   dev_dbg (&(lun)->dev, fmt, ## args)
-#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
-#define LWARN(lun, fmt, args...)  dev_warn(&(lun)->dev, fmt, ## args)
-#define LINFO(lun, fmt, args...)  dev_info(&(lun)->dev, fmt, ## args)
-
-
-#ifdef DUMP_MSGS
-
-#  define dump_msg(fsg, /* const char * */ label,                      \
-                  /* const u8 * */ buf, /* unsigned */ length) do {    \
-       if (length < 512) {                                             \
-               DBG(fsg, "%s, length %u:\n", label, length);            \
-               print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,      \
-                              16, 1, buf, length, 0);                  \
-       }                                                               \
-} while (0)
-
-#  define dump_cdb(fsg) do { } while (0)
-
-#else
-
-#  define dump_msg(fsg, /* const char * */ label, \
-                  /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
-
-#  ifdef VERBOSE_DEBUG
-
-#    define dump_cdb(fsg)                                              \
-       print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,      \
-                      16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)         \
-
-#  else
-
-#    define dump_cdb(fsg) do { } while (0)
-
-#  endif /* VERBOSE_DEBUG */
-
-#endif /* DUMP_MSGS */
-
-/*-------------------------------------------------------------------------*/
-
-/* Length of a SCSI Command Data Block */
-#define MAX_COMMAND_SIZE       16
-
-/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
-#define SS_NO_SENSE                            0
-#define SS_COMMUNICATION_FAILURE               0x040800
-#define SS_INVALID_COMMAND                     0x052000
-#define SS_INVALID_FIELD_IN_CDB                        0x052400
-#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE  0x052100
-#define SS_LOGICAL_UNIT_NOT_SUPPORTED          0x052500
-#define SS_MEDIUM_NOT_PRESENT                  0x023a00
-#define SS_MEDIUM_REMOVAL_PREVENTED            0x055302
-#define SS_NOT_READY_TO_READY_TRANSITION       0x062800
-#define SS_RESET_OCCURRED                      0x062900
-#define SS_SAVING_PARAMETERS_NOT_SUPPORTED     0x053900
-#define SS_UNRECOVERED_READ_ERROR              0x031100
-#define SS_WRITE_ERROR                         0x030c02
-#define SS_WRITE_PROTECTED                     0x072700
-
-#define SK(x)          ((u8) ((x) >> 16))      /* Sense Key byte, etc. */
-#define ASC(x)         ((u8) ((x) >> 8))
-#define ASCQ(x)                ((u8) (x))
-
-
-/*-------------------------------------------------------------------------*/
-
-
-struct fsg_lun {
-       struct file     *filp;
-       loff_t          file_length;
-       loff_t          num_sectors;
-
-       unsigned int    initially_ro:1;
-       unsigned int    ro:1;
-       unsigned int    removable:1;
-       unsigned int    cdrom:1;
-       unsigned int    prevent_medium_removal:1;
-       unsigned int    registered:1;
-       unsigned int    info_valid:1;
-       unsigned int    nofua:1;
-
-       u32             sense_data;
-       u32             sense_data_info;
-       u32             unit_attention_data;
-
-       unsigned int    blkbits;        /* Bits of logical block size of bound block device */
-       unsigned int    blksize;        /* logical block size of bound block device */
-       struct device   dev;
-};
-
-static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
-{
-       return curlun->filp != NULL;
-}
-
-static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
-{
-       return container_of(dev, struct fsg_lun, dev);
-}
-
-
-/* Big enough to hold our biggest descriptor */
-#define EP0_BUFSIZE    256
-#define DELAYED_STATUS (EP0_BUFSIZE + 999)     /* An impossibly large value */
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-
-static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
-module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
-MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
-
-#else
-
-/*
- * Number of buffers we will use.
- * 2 is usually enough for good buffering pipeline
- */
-#define fsg_num_buffers        CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
-
-#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
-
-/* check if fsg_num_buffers is within a valid range */
-static inline int fsg_num_buffers_validate(void)
-{
-       if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
-               return 0;
-       pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
-              fsg_num_buffers, 2 ,4);
-       return -EINVAL;
-}
-
-/* Default size of buffer length. */
-#define FSG_BUFLEN     ((u32)16384)
-
-/* Maximal number of LUNs supported in mass storage function */
-#define FSG_MAX_LUNS   8
-
-enum fsg_buffer_state {
-       BUF_STATE_EMPTY = 0,
-       BUF_STATE_FULL,
-       BUF_STATE_BUSY
-};
-
-struct fsg_buffhd {
-       void                            *buf;
-       enum fsg_buffer_state           state;
-       struct fsg_buffhd               *next;
-
-       /*
-        * The NetChip 2280 is faster, and handles some protocol faults
-        * better, if we don't submit any short bulk-out read requests.
-        * So we will record the intended request length here.
-        */
-       unsigned int                    bulk_out_intended_length;
-
-       struct usb_request              *inreq;
-       int                             inreq_busy;
-       struct usb_request              *outreq;
-       int                             outreq_busy;
-};
-
-enum fsg_state {
-       /* This one isn't used anywhere */
-       FSG_STATE_COMMAND_PHASE = -10,
-       FSG_STATE_DATA_PHASE,
-       FSG_STATE_STATUS_PHASE,
-
-       FSG_STATE_IDLE = 0,
-       FSG_STATE_ABORT_BULK_OUT,
-       FSG_STATE_RESET,
-       FSG_STATE_INTERFACE_CHANGE,
-       FSG_STATE_CONFIG_CHANGE,
-       FSG_STATE_DISCONNECT,
-       FSG_STATE_EXIT,
-       FSG_STATE_TERMINATED
-};
-
-enum data_direction {
-       DATA_DIR_UNKNOWN = 0,
-       DATA_DIR_FROM_HOST,
-       DATA_DIR_TO_HOST,
-       DATA_DIR_NONE
-};
-
-
-/*-------------------------------------------------------------------------*/
-
-
-static inline u32 get_unaligned_be24(u8 *buf)
-{
-       return 0xffffff & (u32) get_unaligned_be32(buf - 1);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-
-enum {
-       FSG_STRING_INTERFACE
-};
-
+#include "storage_common.h"
 
 /* There is only one interface. */
 
-static struct usb_interface_descriptor
-fsg_intf_desc = {
+struct usb_interface_descriptor fsg_intf_desc = {
        .bLength =              sizeof fsg_intf_desc,
        .bDescriptorType =      USB_DT_INTERFACE,
 
@@ -268,14 +43,14 @@ fsg_intf_desc = {
        .bInterfaceProtocol =   USB_PR_BULK,    /* Adjusted during fsg_bind() */
        .iInterface =           FSG_STRING_INTERFACE,
 };
+EXPORT_SYMBOL(fsg_intf_desc);
 
 /*
  * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
  * interrupt-in.
  */
 
-static struct usb_endpoint_descriptor
-fsg_fs_bulk_in_desc = {
+struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
@@ -283,9 +58,9 @@ fsg_fs_bulk_in_desc = {
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        /* wMaxPacketSize set by autoconfiguration */
 };
+EXPORT_SYMBOL(fsg_fs_bulk_in_desc);
 
-static struct usb_endpoint_descriptor
-fsg_fs_bulk_out_desc = {
+struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
@@ -293,13 +68,15 @@ fsg_fs_bulk_out_desc = {
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        /* wMaxPacketSize set by autoconfiguration */
 };
+EXPORT_SYMBOL(fsg_fs_bulk_out_desc);
 
-static struct usb_descriptor_header *fsg_fs_function[] = {
+struct usb_descriptor_header *fsg_fs_function[] = {
        (struct usb_descriptor_header *) &fsg_intf_desc,
        (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
        (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
        NULL,
 };
+EXPORT_SYMBOL(fsg_fs_function);
 
 
 /*
@@ -310,8 +87,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = {
  * and a "device qualifier" ... plus more construction options
  * for the configuration descriptor.
  */
-static struct usb_endpoint_descriptor
-fsg_hs_bulk_in_desc = {
+struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
@@ -319,9 +95,9 @@ fsg_hs_bulk_in_desc = {
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        .wMaxPacketSize =       cpu_to_le16(512),
 };
+EXPORT_SYMBOL(fsg_hs_bulk_in_desc);
 
-static struct usb_endpoint_descriptor
-fsg_hs_bulk_out_desc = {
+struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
@@ -330,17 +106,18 @@ fsg_hs_bulk_out_desc = {
        .wMaxPacketSize =       cpu_to_le16(512),
        .bInterval =            1,      /* NAK every 1 uframe */
 };
+EXPORT_SYMBOL(fsg_hs_bulk_out_desc);
 
 
-static struct usb_descriptor_header *fsg_hs_function[] = {
+struct usb_descriptor_header *fsg_hs_function[] = {
        (struct usb_descriptor_header *) &fsg_intf_desc,
        (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
        (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
        NULL,
 };
+EXPORT_SYMBOL(fsg_hs_function);
 
-static struct usb_endpoint_descriptor
-fsg_ss_bulk_in_desc = {
+struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
@@ -348,16 +125,17 @@ fsg_ss_bulk_in_desc = {
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        .wMaxPacketSize =       cpu_to_le16(1024),
 };
+EXPORT_SYMBOL(fsg_ss_bulk_in_desc);
 
-static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
        .bLength =              sizeof(fsg_ss_bulk_in_comp_desc),
        .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
 
        /*.bMaxBurst =          DYNAMIC, */
 };
+EXPORT_SYMBOL(fsg_ss_bulk_in_comp_desc);
 
-static struct usb_endpoint_descriptor
-fsg_ss_bulk_out_desc = {
+struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
@@ -365,15 +143,17 @@ fsg_ss_bulk_out_desc = {
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        .wMaxPacketSize =       cpu_to_le16(1024),
 };
+EXPORT_SYMBOL(fsg_ss_bulk_out_desc);
 
-static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
        .bLength =              sizeof(fsg_ss_bulk_in_comp_desc),
        .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
 
        /*.bMaxBurst =          DYNAMIC, */
 };
+EXPORT_SYMBOL(fsg_ss_bulk_out_comp_desc);
 
-static struct usb_descriptor_header *fsg_ss_function[] = {
+struct usb_descriptor_header *fsg_ss_function[] = {
        (struct usb_descriptor_header *) &fsg_intf_desc,
        (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
        (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
@@ -381,17 +161,7 @@ static struct usb_descriptor_header *fsg_ss_function[] = {
        (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
        NULL,
 };
-
-/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
-static struct usb_string               fsg_strings[] = {
-       {FSG_STRING_INTERFACE,          fsg_string_interface},
-       {}
-};
-
-static struct usb_gadget_strings       fsg_stringtab = {
-       .language       = 0x0409,               /* en-us */
-       .strings        = fsg_strings,
-};
+EXPORT_SYMBOL(fsg_ss_function);
 
 
  /*-------------------------------------------------------------------------*/
@@ -401,7 +171,7 @@ static struct usb_gadget_strings    fsg_stringtab = {
  * the caller must own fsg->filesem for writing.
  */
 
-static void fsg_lun_close(struct fsg_lun *curlun)
+void fsg_lun_close(struct fsg_lun *curlun)
 {
        if (curlun->filp) {
                LDBG(curlun, "close backing file\n");
@@ -409,9 +179,9 @@ static void fsg_lun_close(struct fsg_lun *curlun)
                curlun->filp = NULL;
        }
 }
+EXPORT_SYMBOL(fsg_lun_close);
 
-
-static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
 {
        int                             ro;
        struct file                     *filp = NULL;
@@ -508,6 +278,7 @@ out:
        fput(filp);
        return rc;
 }
+EXPORT_SYMBOL(fsg_lun_open);
 
 
 /*-------------------------------------------------------------------------*/
@@ -516,7 +287,7 @@ out:
  * Sync the file data, don't bother with the metadata.
  * This code was copied from fs/buffer.c:sys_fdatasync().
  */
-static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+int fsg_lun_fsync_sub(struct fsg_lun *curlun)
 {
        struct file     *filp = curlun->filp;
 
@@ -524,8 +295,9 @@ static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
                return 0;
        return vfs_fsync(filp, 1);
 }
+EXPORT_SYMBOL(fsg_lun_fsync_sub);
 
-static void store_cdrom_address(u8 *dest, int msf, u32 addr)
+void store_cdrom_address(u8 *dest, int msf, u32 addr)
 {
        if (msf) {
                /* Convert to Minutes-Seconds-Frames */
@@ -542,34 +314,28 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr)
                put_unaligned_be32(addr, dest);
        }
 }
-
+EXPORT_SYMBOL(store_cdrom_address);
 
 /*-------------------------------------------------------------------------*/
 
 
-static ssize_t ro_show(struct device *dev, struct device_attribute *attr,
-                      char *buf)
+ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
 {
-       struct fsg_lun  *curlun = fsg_lun_from_dev(dev);
-
        return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
                                  ? curlun->ro
                                  : curlun->initially_ro);
 }
+EXPORT_SYMBOL(fsg_show_ro);
 
-static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
-                         char *buf)
+ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
 {
-       struct fsg_lun  *curlun = fsg_lun_from_dev(dev);
-
        return sprintf(buf, "%u\n", curlun->nofua);
 }
+EXPORT_SYMBOL(fsg_show_nofua);
 
-static ssize_t file_show(struct device *dev, struct device_attribute *attr,
-                        char *buf)
+ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                     char *buf)
 {
-       struct fsg_lun  *curlun = fsg_lun_from_dev(dev);
-       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
        char            *p;
        ssize_t         rc;
 
@@ -591,17 +357,44 @@ static ssize_t file_show(struct device *dev, struct device_attribute *attr,
        up_read(filesem);
        return rc;
 }
+EXPORT_SYMBOL(fsg_show_file);
 
+ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%u\n", curlun->cdrom);
+}
+EXPORT_SYMBOL(fsg_show_cdrom);
 
-static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
-                       const char *buf, size_t count)
+ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%u\n", curlun->removable);
+}
+EXPORT_SYMBOL(fsg_show_removable);
+
+/*
+ * The caller must hold fsg->filesem for reading when calling this function.
+ */
+static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro)
+{
+       if (fsg_lun_is_open(curlun)) {
+               LDBG(curlun, "read-only status change prevented\n");
+               return -EBUSY;
+       }
+
+       curlun->ro = ro;
+       curlun->initially_ro = ro;
+       LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+
+       return 0;
+}
+
+ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                    const char *buf, size_t count)
 {
        ssize_t         rc;
-       struct fsg_lun  *curlun = fsg_lun_from_dev(dev);
-       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
-       unsigned        ro;
+       bool            ro;
 
-       rc = kstrtouint(buf, 2, &ro);
+       rc = strtobool(buf, &ro);
        if (rc)
                return rc;
 
@@ -610,27 +403,21 @@ static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
         * backing file is closed.
         */
        down_read(filesem);
-       if (fsg_lun_is_open(curlun)) {
-               LDBG(curlun, "read-only status change prevented\n");
-               rc = -EBUSY;
-       } else {
-               curlun->ro = ro;
-               curlun->initially_ro = ro;
-               LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+       rc = _fsg_store_ro(curlun, ro);
+       if (!rc)
                rc = count;
-       }
        up_read(filesem);
+
        return rc;
 }
+EXPORT_SYMBOL(fsg_store_ro);
 
-static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
-                          const char *buf, size_t count)
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
 {
-       struct fsg_lun  *curlun = fsg_lun_from_dev(dev);
-       unsigned        nofua;
+       bool            nofua;
        int             ret;
 
-       ret = kstrtouint(buf, 2, &nofua);
+       ret = strtobool(buf, &nofua);
        if (ret)
                return ret;
 
@@ -642,12 +429,11 @@ static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
 
        return count;
 }
+EXPORT_SYMBOL(fsg_store_nofua);
 
-static ssize_t file_store(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t count)
+ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                      const char *buf, size_t count)
 {
-       struct fsg_lun  *curlun = fsg_lun_from_dev(dev);
-       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
        int             rc = 0;
 
        if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
@@ -674,3 +460,45 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr,
        up_write(filesem);
        return (rc < 0 ? rc : count);
 }
+EXPORT_SYMBOL(fsg_store_file);
+
+ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                       const char *buf, size_t count)
+{
+       bool            cdrom;
+       int             ret;
+
+       ret = strtobool(buf, &cdrom);
+       if (ret)
+               return ret;
+
+       down_read(filesem);
+       ret = cdrom ? _fsg_store_ro(curlun, true) : 0;
+
+       if (!ret) {
+               curlun->cdrom = cdrom;
+               ret = count;
+       }
+       up_read(filesem);
+
+       return ret;
+}
+EXPORT_SYMBOL(fsg_store_cdrom);
+
+ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
+                           size_t count)
+{
+       bool            removable;
+       int             ret;
+
+       ret = strtobool(buf, &removable);
+       if (ret)
+               return ret;
+
+       curlun->removable = removable;
+
+       return count;
+}
+EXPORT_SYMBOL(fsg_store_removable);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/storage_common.h
new file mode 100644 (file)
index 0000000..c74c2fd
--- /dev/null
@@ -0,0 +1,229 @@
+#ifndef USB_STORAGE_COMMON_H
+#define USB_STORAGE_COMMON_H
+
+#include <linux/device.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <asm/unaligned.h>
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG  LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define _LMSG(func, lun, fmt, args...)                                 \
+       do {                                                            \
+               if ((lun)->name_pfx && *(lun)->name_pfx)                \
+                       func("%s/%s: " fmt, *(lun)->name_pfx,           \
+                                (lun)->name, ## args);                 \
+               else                                                    \
+                       func("%s: " fmt, (lun)->name, ## args);         \
+       } while (0)
+
+#define LDBG(lun, fmt, args...)                _LMSG(pr_debug, lun, fmt, ## args)
+#define LERROR(lun, fmt, args...)      _LMSG(pr_err, lun, fmt, ## args)
+#define LWARN(lun, fmt, args...)       _LMSG(pr_warn, lun, fmt, ## args)
+#define LINFO(lun, fmt, args...)       _LMSG(pr_info, lun, fmt, ## args)
+
+
+#ifdef DUMP_MSGS
+
+#  define dump_msg(fsg, /* const char * */ label,                      \
+                  /* const u8 * */ buf, /* unsigned */ length)         \
+do {                                                                   \
+       if (length < 512) {                                             \
+               DBG(fsg, "%s, length %u:\n", label, length);            \
+               print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,      \
+                              16, 1, buf, length, 0);                  \
+       }                                                               \
+} while (0)
+
+#  define dump_cdb(fsg) do { } while (0)
+
+#else
+
+#  define dump_msg(fsg, /* const char * */ label, \
+                  /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+#  ifdef VERBOSE_DEBUG
+
+#    define dump_cdb(fsg)                                              \
+       print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,      \
+                      16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)         \
+
+#  else
+
+#    define dump_cdb(fsg) do { } while (0)
+
+#  endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE       16
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE                            0
+#define SS_COMMUNICATION_FAILURE               0x040800
+#define SS_INVALID_COMMAND                     0x052000
+#define SS_INVALID_FIELD_IN_CDB                        0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE  0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED          0x052500
+#define SS_MEDIUM_NOT_PRESENT                  0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED            0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION       0x062800
+#define SS_RESET_OCCURRED                      0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED     0x053900
+#define SS_UNRECOVERED_READ_ERROR              0x031100
+#define SS_WRITE_ERROR                         0x030c02
+#define SS_WRITE_PROTECTED                     0x072700
+
+#define SK(x)          ((u8) ((x) >> 16))      /* Sense Key byte, etc. */
+#define ASC(x)         ((u8) ((x) >> 8))
+#define ASCQ(x)                ((u8) (x))
+
+struct fsg_lun {
+       struct file     *filp;
+       loff_t          file_length;
+       loff_t          num_sectors;
+
+       unsigned int    initially_ro:1;
+       unsigned int    ro:1;
+       unsigned int    removable:1;
+       unsigned int    cdrom:1;
+       unsigned int    prevent_medium_removal:1;
+       unsigned int    registered:1;
+       unsigned int    info_valid:1;
+       unsigned int    nofua:1;
+
+       u32             sense_data;
+       u32             sense_data_info;
+       u32             unit_attention_data;
+
+       unsigned int    blkbits; /* Bits of logical block size
+                                                      of bound block device */
+       unsigned int    blksize; /* logical block size of bound block device */
+       struct device   dev;
+       const char      *name;          /* "lun.name" */
+       const char      **name_pfx;     /* "function.name" */
+};
+
+static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
+{
+       return curlun->filp != NULL;
+}
+
+/* Big enough to hold our biggest descriptor */
+#define EP0_BUFSIZE    256
+#define DELAYED_STATUS (EP0_BUFSIZE + 999)     /* An impossibly large value */
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN     ((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS   8
+
+enum fsg_buffer_state {
+       BUF_STATE_EMPTY = 0,
+       BUF_STATE_FULL,
+       BUF_STATE_BUSY
+};
+
+struct fsg_buffhd {
+       void                            *buf;
+       enum fsg_buffer_state           state;
+       struct fsg_buffhd               *next;
+
+       /*
+        * The NetChip 2280 is faster, and handles some protocol faults
+        * better, if we don't submit any short bulk-out read requests.
+        * So we will record the intended request length here.
+        */
+       unsigned int                    bulk_out_intended_length;
+
+       struct usb_request              *inreq;
+       int                             inreq_busy;
+       struct usb_request              *outreq;
+       int                             outreq_busy;
+};
+
+enum fsg_state {
+       /* This one isn't used anywhere */
+       FSG_STATE_COMMAND_PHASE = -10,
+       FSG_STATE_DATA_PHASE,
+       FSG_STATE_STATUS_PHASE,
+
+       FSG_STATE_IDLE = 0,
+       FSG_STATE_ABORT_BULK_OUT,
+       FSG_STATE_RESET,
+       FSG_STATE_INTERFACE_CHANGE,
+       FSG_STATE_CONFIG_CHANGE,
+       FSG_STATE_DISCONNECT,
+       FSG_STATE_EXIT,
+       FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+       DATA_DIR_UNKNOWN = 0,
+       DATA_DIR_FROM_HOST,
+       DATA_DIR_TO_HOST,
+       DATA_DIR_NONE
+};
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+       return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+       return container_of(dev, struct fsg_lun, dev);
+}
+
+enum {
+       FSG_STRING_INTERFACE
+};
+
+extern struct usb_interface_descriptor fsg_intf_desc;
+
+extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc;
+extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc;
+extern struct usb_descriptor_header *fsg_fs_function[];
+
+extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc;
+extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc;
+extern struct usb_descriptor_header *fsg_hs_function[];
+
+extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc;
+extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc;
+extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc;
+extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc;
+extern struct usb_descriptor_header *fsg_ss_function[];
+
+void fsg_lun_close(struct fsg_lun *curlun);
+int fsg_lun_open(struct fsg_lun *curlun, const char *filename);
+int fsg_lun_fsync_sub(struct fsg_lun *curlun);
+void store_cdrom_address(u8 *dest, int msf, u32 addr);
+ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                     char *buf);
+ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                    const char *buf, size_t count);
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count);
+ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                      const char *buf, size_t count);
+ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                       const char *buf, size_t count);
+ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
+                           size_t count);
+
+#endif /* USB_STORAGE_COMMON_H */
index 0ff33396eef3ac6e99a78af1db7874d82cff8480..eccea1df702df3afc45c692de31b23febfb52c37 100644 (file)
@@ -472,7 +472,7 @@ static int usbg_bot_setup(struct usb_function *f,
                bot_enqueue_cmd_cbw(fu);
                return 0;
                break;
-       };
+       }
        return -ENOTSUPP;
 }
 
@@ -617,7 +617,7 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
 
        default:
                BUG();
-       };
+       }
        return;
 
 cleanup:
index 59891b1c48fc2e7a04febe488b979b23f3a1cb6f..27768a7d986a7f84e66d510be82cab2c513c7367 100644 (file)
@@ -356,7 +356,8 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
        kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
        return 0;
 err1:
-       dev_err(&udc->dev, "failed to start %s: %d\n",
+       if (ret != -EISNAM)
+               dev_err(&udc->dev, "failed to start %s: %d\n",
                        udc->driver->function, ret);
        udc->driver = NULL;
        udc->dev.driver = NULL;
index 0deb9d6cde26245fd1c52c0b541eecd100aedce6..0dd07ae1555ddf066312e0ff4e8182a02f27a3d6 100644 (file)
@@ -95,6 +95,18 @@ unsigned autoresume = DEFAULT_AUTORESUME;
 module_param(autoresume, uint, S_IRUGO);
 MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
 
+/* Maximum Autoresume time */
+unsigned max_autoresume;
+module_param(max_autoresume, uint, S_IRUGO);
+MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup");
+
+/* Interval between two remote wakeups */
+unsigned autoresume_interval_ms;
+module_param(autoresume_interval_ms, uint, S_IRUGO);
+MODULE_PARM_DESC(autoresume_interval_ms,
+               "milliseconds to increase successive wakeup delays");
+
+static unsigned autoresume_step_ms;
 /*-------------------------------------------------------------------------*/
 
 static struct usb_device_descriptor device_desc = {
@@ -183,8 +195,16 @@ static void zero_suspend(struct usb_composite_dev *cdev)
                return;
 
        if (autoresume) {
-               mod_timer(&autoresume_timer, jiffies + (HZ * autoresume));
-               DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
+               if (max_autoresume &&
+                       (autoresume_step_ms > max_autoresume * 1000))
+                               autoresume_step_ms = autoresume * 1000;
+
+               mod_timer(&autoresume_timer, jiffies +
+                       msecs_to_jiffies(autoresume_step_ms));
+               DBG(cdev, "suspend, wakeup in %d milliseconds\n",
+                       autoresume_step_ms);
+
+               autoresume_step_ms += autoresume_interval_ms;
        } else
                DBG(cdev, "%s\n", __func__);
 }
@@ -316,6 +336,7 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
        if (autoresume) {
                sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
                loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+               autoresume_step_ms = autoresume * 1000;
        }
 
        /* support OTG systems */
index b3f20d7f15dee9554ef9ef96acd735c3e3926064..a9707da7da0bff969c68d6d5a0fa66f2816ef8b7 100644 (file)
@@ -54,7 +54,7 @@ config USB_EHCI_HCD
 
 config USB_EHCI_ROOT_HUB_TT
        bool "Root Hub Transaction Translators"
-       depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST
+       depends on USB_EHCI_HCD
        ---help---
          Some EHCI chips have vendor-specific extensions to integrate
          transaction translators, so that no OHCI or UHCI companion
@@ -66,7 +66,7 @@ config USB_EHCI_ROOT_HUB_TT
 
 config USB_EHCI_TT_NEWSCHED
        bool "Improved Transaction Translator scheduling"
-       depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST
+       depends on USB_EHCI_HCD
        default y
        ---help---
          This changes the periodic scheduling code to fill more of the low
@@ -203,12 +203,11 @@ config USB_EHCI_SH
          Enables support for the on-chip EHCI controller on the SuperH.
          If you use the PCI EHCI controller, this option is not necessary.
 
-config USB_EHCI_S5P
+config USB_EHCI_EXYNOS
        tristate "EHCI support for Samsung S5P/EXYNOS SoC Series"
        depends on PLAT_S5P || ARCH_EXYNOS
        help
-       Enable support for the Samsung S5Pxxxx and Exynos3/4/5 SOC's
-       on-chip EHCI controller.
+       Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
 
 config USB_EHCI_MV
        bool "EHCI support for Marvell PXA/MMP USB controller"
@@ -224,7 +223,7 @@ config USB_EHCI_MV
          on-chip EHCI USB controller" for those.
 
 config USB_W90X900_EHCI
-       bool "W90X900(W90P910) EHCI support"
+       tristate "W90X900(W90P910) EHCI support"
        depends on ARCH_W90X900
        ---help---
                Enables support for the W90X900 USB controller
@@ -367,14 +366,54 @@ config USB_OHCI_HCD
 if USB_OHCI_HCD
 
 config USB_OHCI_HCD_OMAP1
-       bool "OHCI support for OMAP1/2 chips"
+       tristate "OHCI support for OMAP1/2 chips"
        depends on ARCH_OMAP1
        default y
        ---help---
          Enables support for the OHCI controller on OMAP1/2 chips.
 
+config USB_OHCI_HCD_SPEAR
+        tristate "Support for ST SPEAr on-chip OHCI USB controller"
+        depends on USB_OHCI_HCD && PLAT_SPEAR
+        default y
+        ---help---
+          Enables support for the on-chip OHCI controller on
+          ST SPEAr chips.
+
+config USB_OHCI_HCD_S3C2410
+        tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series"
+        depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX)
+        default y
+        ---help---
+          Enables support for the on-chip OHCI controller on
+          S3C24xx/S3C64xx chips.
+
+config USB_OHCI_HCD_LPC32XX
+       tristate "Support for LPC on-chip OHCI USB controller"
+       depends on USB_OHCI_HCD && ARCH_LPC32XX
+       default y
+       ---help---
+          Enables support for the on-chip OHCI controller on
+          NXP chips.
+
+config USB_OHCI_HCD_PXA27X
+       tristate "Support for PXA27X/PXA3XX on-chip OHCI USB controller"
+       depends on USB_OHCI_HCD && (PXA27x || PXA3xx)
+       default y
+       ---help---
+         Enables support for the on-chip OHCI controller on
+         PXA27x/PXA3xx chips.
+
+config USB_OHCI_HCD_AT91
+        tristate "Support for Atmel on-chip OHCI USB controller"
+        depends on USB_OHCI_HCD && ARCH_AT91
+        default y
+        ---help---
+          Enables support for the on-chip OHCI controller on
+          Atmel chips.
+
 config USB_OHCI_HCD_OMAP3
-       bool "OHCI support for OMAP3 and later chips"
+       tristate "OHCI support for OMAP3 and later chips"
        depends on (ARCH_OMAP3 || ARCH_OMAP4)
        default y
        ---help---
@@ -454,8 +493,8 @@ config USB_OHCI_SH
          If you use the PCI OHCI controller, this option is not necessary.
 
 config USB_OHCI_EXYNOS
-       boolean "OHCI support for Samsung EXYNOS SoC Series"
-       depends on ARCH_EXYNOS
+       tristate "OHCI support for Samsung S5P/EXYNOS SoC Series"
+       depends on PLAT_S5P || ARCH_EXYNOS
        help
         Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
 
index 50b0041c09a95464ed9ff353b090818e39d0112c..01e879ef3654865131eac3523050f2bad636531b 100644 (file)
@@ -34,10 +34,11 @@ obj-$(CONFIG_USB_EHCI_MXC)  += ehci-mxc.o
 obj-$(CONFIG_USB_EHCI_HCD_OMAP)        += ehci-omap.o
 obj-$(CONFIG_USB_EHCI_HCD_ORION)       += ehci-orion.o
 obj-$(CONFIG_USB_EHCI_HCD_SPEAR)       += ehci-spear.o
-obj-$(CONFIG_USB_EHCI_S5P)     += ehci-s5p.o
+obj-$(CONFIG_USB_EHCI_EXYNOS)  += ehci-exynos.o
 obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
 obj-$(CONFIG_USB_EHCI_MSM)     += ehci-msm.o
 obj-$(CONFIG_USB_EHCI_TEGRA)   += ehci-tegra.o
+obj-$(CONFIG_USB_W90X900_EHCI) += ehci-w90x900.o
 
 obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
 obj-$(CONFIG_USB_ISP116X_HCD)  += isp116x-hcd.o
@@ -46,6 +47,14 @@ obj-$(CONFIG_USB_ISP1362_HCD)        += isp1362-hcd.o
 obj-$(CONFIG_USB_OHCI_HCD)     += ohci-hcd.o
 obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o
 obj-$(CONFIG_USB_OHCI_HCD_PLATFORM)    += ohci-platform.o
+obj-$(CONFIG_USB_OHCI_EXYNOS)  += ohci-exynos.o
+obj-$(CONFIG_USB_OHCI_HCD_OMAP1)       += ohci-omap.o
+obj-$(CONFIG_USB_OHCI_HCD_OMAP3)       += ohci-omap3.o
+obj-$(CONFIG_USB_OHCI_HCD_SPEAR)       += ohci-spear.o
+obj-$(CONFIG_USB_OHCI_HCD_AT91)        += ohci-at91.o
+obj-$(CONFIG_USB_OHCI_HCD_S3C2410)     += ohci-s3c2410.o
+obj-$(CONFIG_USB_OHCI_HCD_LPC32XX)     += ohci-nxp.o
+obj-$(CONFIG_USB_OHCI_HCD_PXA27X)      += ohci-pxa27x.o
 
 obj-$(CONFIG_USB_UHCI_HCD)     += uhci-hcd.o
 obj-$(CONFIG_USB_FHCI_HCD)     += fhci.o
index 3b645ff46f7b9f8df67d2d793c1758be26173b21..f417526fb1f405f2fe1d3b4241e7bdac01c9aeaf 100644 (file)
@@ -30,13 +30,17 @@ static const char hcd_name[] = "ehci-atmel";
 static struct hc_driver __read_mostly ehci_atmel_hc_driver;
 
 /* interface and function clocks */
-static struct clk *iclk, *fclk;
+static struct clk *iclk, *fclk, *uclk;
 static int clocked;
 
 /*-------------------------------------------------------------------------*/
 
 static void atmel_start_clock(void)
 {
+       if (IS_ENABLED(CONFIG_COMMON_CLK)) {
+               clk_set_rate(uclk, 48000000);
+               clk_prepare_enable(uclk);
+       }
        clk_prepare_enable(iclk);
        clk_prepare_enable(fclk);
        clocked = 1;
@@ -46,6 +50,8 @@ static void atmel_stop_clock(void)
 {
        clk_disable_unprepare(fclk);
        clk_disable_unprepare(iclk);
+       if (IS_ENABLED(CONFIG_COMMON_CLK))
+               clk_disable_unprepare(uclk);
        clocked = 0;
 }
 
@@ -130,6 +136,14 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
                retval = -ENOENT;
                goto fail_request_resource;
        }
+       if (IS_ENABLED(CONFIG_COMMON_CLK)) {
+               uclk = devm_clk_get(&pdev->dev, "usb_clk");
+               if (IS_ERR(uclk)) {
+                       dev_err(&pdev->dev, "failed to get uclk\n");
+                       retval = PTR_ERR(uclk);
+                       goto fail_request_resource;
+               }
+       }
 
        ehci = hcd_to_ehci(hcd);
        /* registers start at offset 0x0 */
index aa5b603f39336f69ffc2dce8a787f64694fa7d23..4a9c2edbcb2bccf8cdf678aa04266fb5d78560d8 100644 (file)
@@ -334,6 +334,7 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { }
 /* troubleshooting help: expose state in debugfs */
 
 static int debug_async_open(struct inode *, struct file *);
+static int debug_bandwidth_open(struct inode *, struct file *);
 static int debug_periodic_open(struct inode *, struct file *);
 static int debug_registers_open(struct inode *, struct file *);
 
@@ -347,6 +348,13 @@ static const struct file_operations debug_async_fops = {
        .release        = debug_close,
        .llseek         = default_llseek,
 };
+static const struct file_operations debug_bandwidth_fops = {
+       .owner          = THIS_MODULE,
+       .open           = debug_bandwidth_open,
+       .read           = debug_output,
+       .release        = debug_close,
+       .llseek         = default_llseek,
+};
 static const struct file_operations debug_periodic_fops = {
        .owner          = THIS_MODULE,
        .open           = debug_periodic_open,
@@ -379,7 +387,7 @@ struct debug_buffer {
                case QH_LOW_SPEED:  tmp = 'l'; break; \
                case QH_HIGH_SPEED: tmp = 'h'; break; \
                default: tmp = '?'; break; \
-               }; tmp; })
+               } tmp; })
 
 static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
 {
@@ -525,6 +533,89 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
        return strlen(buf->output_buf);
 }
 
+static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf)
+{
+       struct ehci_hcd         *ehci;
+       struct ehci_tt          *tt;
+       struct ehci_per_sched   *ps;
+       unsigned                temp, size;
+       char                    *next;
+       unsigned                i;
+       u8                      *bw;
+       u16                     *bf;
+       u8                      budget[EHCI_BANDWIDTH_SIZE];
+
+       ehci = hcd_to_ehci(bus_to_hcd(buf->bus));
+       next = buf->output_buf;
+       size = buf->alloc_size;
+
+       *next = 0;
+
+       spin_lock_irq(&ehci->lock);
+
+       /* Dump the HS bandwidth table */
+       temp = scnprintf(next, size,
+                       "HS bandwidth allocation (us per microframe)\n");
+       size -= temp;
+       next += temp;
+       for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
+               bw = &ehci->bandwidth[i];
+               temp = scnprintf(next, size,
+                               "%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
+                               i, bw[0], bw[1], bw[2], bw[3],
+                                       bw[4], bw[5], bw[6], bw[7]);
+               size -= temp;
+               next += temp;
+       }
+
+       /* Dump all the FS/LS tables */
+       list_for_each_entry(tt, &ehci->tt_list, tt_list) {
+               temp = scnprintf(next, size,
+                               "\nTT %s port %d  FS/LS bandwidth allocation (us per frame)\n",
+                               dev_name(&tt->usb_tt->hub->dev),
+                               tt->tt_port + !!tt->usb_tt->multi);
+               size -= temp;
+               next += temp;
+
+               bf = tt->bandwidth;
+               temp = scnprintf(next, size,
+                               "  %5u%5u%5u%5u%5u%5u%5u%5u\n",
+                               bf[0], bf[1], bf[2], bf[3],
+                                       bf[4], bf[5], bf[6], bf[7]);
+               size -= temp;
+               next += temp;
+
+               temp = scnprintf(next, size,
+                               "FS/LS budget (us per microframe)\n");
+               size -= temp;
+               next += temp;
+               compute_tt_budget(budget, tt);
+               for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
+                       bw = &budget[i];
+                       temp = scnprintf(next, size,
+                                       "%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
+                                       i, bw[0], bw[1], bw[2], bw[3],
+                                               bw[4], bw[5], bw[6], bw[7]);
+                       size -= temp;
+                       next += temp;
+               }
+               list_for_each_entry(ps, &tt->ps_list, ps_list) {
+                       temp = scnprintf(next, size,
+                                       "%s ep %02x:  %4u @ %2u.%u+%u mask %04x\n",
+                                       dev_name(&ps->udev->dev),
+                                       ps->ep->desc.bEndpointAddress,
+                                       ps->tt_usecs,
+                                       ps->bw_phase, ps->phase_uf,
+                                       ps->bw_period, ps->cs_mask);
+                       size -= temp;
+                       next += temp;
+               }
+       }
+       spin_unlock_irq(&ehci->lock);
+
+       return next - buf->output_buf;
+}
+
 #define DBG_SCHED_LIMIT 64
 static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
 {
@@ -571,7 +662,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
                        case Q_TYPE_QH:
                                hw = p.qh->hw;
                                temp = scnprintf (next, size, " qh%d-%04x/%p",
-                                               p.qh->period,
+                                               p.qh->ps.period,
                                                hc32_to_cpup(ehci,
                                                        &hw->hw_info2)
                                                        /* uframe masks */
@@ -618,7 +709,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
                                                speed_char (scratch),
                                                scratch & 0x007f,
                                                (scratch >> 8) & 0x000f, type,
-                                               p.qh->usecs, p.qh->c_usecs,
+                                               p.qh->ps.usecs,
+                                               p.qh->ps.c_usecs,
                                                temp,
                                                0x7ff & (scratch >> 16));
 
@@ -645,7 +737,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
                        case Q_TYPE_SITD:
                                temp = scnprintf (next, size,
                                        " sitd%d-%04x/%p",
-                                       p.sitd->stream->interval,
+                                       p.sitd->stream->ps.period,
                                        hc32_to_cpup(ehci, &p.sitd->hw_uframe)
                                                & 0x0000ffff,
                                        p.sitd);
@@ -918,6 +1010,7 @@ static int debug_close(struct inode *inode, struct file *file)
 
        return 0;
 }
+
 static int debug_async_open(struct inode *inode, struct file *file)
 {
        file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);
@@ -925,6 +1018,14 @@ static int debug_async_open(struct inode *inode, struct file *file)
        return file->private_data ? 0 : -ENOMEM;
 }
 
+static int debug_bandwidth_open(struct inode *inode, struct file *file)
+{
+       file->private_data = alloc_buffer(inode->i_private,
+                       fill_bandwidth_buffer);
+
+       return file->private_data ? 0 : -ENOMEM;
+}
+
 static int debug_periodic_open(struct inode *inode, struct file *file)
 {
        struct debug_buffer *buf;
@@ -957,6 +1058,10 @@ static inline void create_debug_files (struct ehci_hcd *ehci)
                                                &debug_async_fops))
                goto file_error;
 
+       if (!debugfs_create_file("bandwidth", S_IRUGO, ehci->debug_dir, bus,
+                                               &debug_bandwidth_fops))
+               goto file_error;
+
        if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
                                                &debug_periodic_fops))
                goto file_error;
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
new file mode 100644 (file)
index 0000000..016352e
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * SAMSUNG EXYNOS USB HOST EHCI Controller
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/samsung_usb_phy.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
+
+#include "ehci.h"
+
+#define DRIVER_DESC "EHCI EXYNOS driver"
+
+#define EHCI_INSNREG00(base)                   (base + 0x90)
+#define EHCI_INSNREG00_ENA_INCR16              (0x1 << 25)
+#define EHCI_INSNREG00_ENA_INCR8               (0x1 << 24)
+#define EHCI_INSNREG00_ENA_INCR4               (0x1 << 23)
+#define EHCI_INSNREG00_ENA_INCRX_ALIGN         (0x1 << 22)
+#define EHCI_INSNREG00_ENABLE_DMA_BURST        \
+       (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \
+        EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
+
+static const char hcd_name[] = "ehci-exynos";
+static struct hc_driver __read_mostly exynos_ehci_hc_driver;
+
+struct exynos_ehci_hcd {
+       struct clk *clk;
+       struct usb_phy *phy;
+       struct usb_otg *otg;
+};
+
+#define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
+
+static void exynos_setup_vbus_gpio(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int err;
+       int gpio;
+
+       if (!dev->of_node)
+               return;
+
+       gpio = of_get_named_gpio(dev->of_node, "samsung,vbus-gpio", 0);
+       if (!gpio_is_valid(gpio))
+               return;
+
+       err = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
+                                   "ehci_vbus_gpio");
+       if (err)
+               dev_err(dev, "can't request ehci vbus gpio %d", gpio);
+}
+
+static int exynos_ehci_probe(struct platform_device *pdev)
+{
+       struct exynos_ehci_hcd *exynos_ehci;
+       struct usb_hcd *hcd;
+       struct ehci_hcd *ehci;
+       struct resource *res;
+       struct usb_phy *phy;
+       int irq;
+       int err;
+
+       /*
+        * Right now device-tree probed devices don't get dma_mask set.
+        * Since shared usb code relies on it, set it here for now.
+        * Once we move to full device tree support this will vanish off.
+        */
+       if (!pdev->dev.dma_mask)
+               pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+       if (!pdev->dev.coherent_dma_mask)
+               pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+       exynos_setup_vbus_gpio(pdev);
+
+       hcd = usb_create_hcd(&exynos_ehci_hc_driver,
+                            &pdev->dev, dev_name(&pdev->dev));
+       if (!hcd) {
+               dev_err(&pdev->dev, "Unable to create HCD\n");
+               return -ENOMEM;
+       }
+       exynos_ehci = to_exynos_ehci(hcd);
+
+       if (of_device_is_compatible(pdev->dev.of_node,
+                                       "samsung,exynos5440-ehci"))
+               goto skip_phy;
+
+       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+       if (IS_ERR(phy)) {
+               usb_put_hcd(hcd);
+               dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
+               return -EPROBE_DEFER;
+       } else {
+               exynos_ehci->phy = phy;
+               exynos_ehci->otg = phy->otg;
+       }
+
+skip_phy:
+
+       exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
+
+       if (IS_ERR(exynos_ehci->clk)) {
+               dev_err(&pdev->dev, "Failed to get usbhost clock\n");
+               err = PTR_ERR(exynos_ehci->clk);
+               goto fail_clk;
+       }
+
+       err = clk_prepare_enable(exynos_ehci->clk);
+       if (err)
+               goto fail_clk;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get I/O memory\n");
+               err = -ENXIO;
+               goto fail_io;
+       }
+
+       hcd->rsrc_start = res->start;
+       hcd->rsrc_len = resource_size(res);
+       hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
+       if (!hcd->regs) {
+               dev_err(&pdev->dev, "Failed to remap I/O memory\n");
+               err = -ENOMEM;
+               goto fail_io;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (!irq) {
+               dev_err(&pdev->dev, "Failed to get IRQ\n");
+               err = -ENODEV;
+               goto fail_io;
+       }
+
+       if (exynos_ehci->otg)
+               exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
+
+       if (exynos_ehci->phy)
+               usb_phy_init(exynos_ehci->phy);
+
+       ehci = hcd_to_ehci(hcd);
+       ehci->caps = hcd->regs;
+
+       /* DMA burst Enable */
+       writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
+
+       err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to add USB HCD\n");
+               goto fail_add_hcd;
+       }
+
+       platform_set_drvdata(pdev, hcd);
+
+       return 0;
+
+fail_add_hcd:
+       if (exynos_ehci->phy)
+               usb_phy_shutdown(exynos_ehci->phy);
+fail_io:
+       clk_disable_unprepare(exynos_ehci->clk);
+fail_clk:
+       usb_put_hcd(hcd);
+       return err;
+}
+
+static int exynos_ehci_remove(struct platform_device *pdev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
+
+       usb_remove_hcd(hcd);
+
+       if (exynos_ehci->otg)
+               exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
+
+       if (exynos_ehci->phy)
+               usb_phy_shutdown(exynos_ehci->phy);
+
+       clk_disable_unprepare(exynos_ehci->clk);
+
+       usb_put_hcd(hcd);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos_ehci_suspend(struct device *dev)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
+
+       bool do_wakeup = device_may_wakeup(dev);
+       int rc;
+
+       rc = ehci_suspend(hcd, do_wakeup);
+
+       if (exynos_ehci->otg)
+               exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
+
+       if (exynos_ehci->phy)
+               usb_phy_shutdown(exynos_ehci->phy);
+
+       clk_disable_unprepare(exynos_ehci->clk);
+
+       return rc;
+}
+
+static int exynos_ehci_resume(struct device *dev)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
+
+       clk_prepare_enable(exynos_ehci->clk);
+
+       if (exynos_ehci->otg)
+               exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
+
+       if (exynos_ehci->phy)
+               usb_phy_init(exynos_ehci->phy);
+
+       /* DMA burst Enable */
+       writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
+
+       ehci_resume(hcd, false);
+       return 0;
+}
+#else
+#define exynos_ehci_suspend    NULL
+#define exynos_ehci_resume     NULL
+#endif
+
+static const struct dev_pm_ops exynos_ehci_pm_ops = {
+       .suspend        = exynos_ehci_suspend,
+       .resume         = exynos_ehci_resume,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_ehci_match[] = {
+       { .compatible = "samsung,exynos4210-ehci" },
+       { .compatible = "samsung,exynos5440-ehci" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_ehci_match);
+#endif
+
+static struct platform_driver exynos_ehci_driver = {
+       .probe          = exynos_ehci_probe,
+       .remove         = exynos_ehci_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
+       .driver = {
+               .name   = "exynos-ehci",
+               .owner  = THIS_MODULE,
+               .pm     = &exynos_ehci_pm_ops,
+               .of_match_table = of_match_ptr(exynos_ehci_match),
+       }
+};
+static const struct ehci_driver_overrides exynos_overrides __initdata = {
+       .extra_priv_size = sizeof(struct exynos_ehci_hcd),
+};
+
+static int __init ehci_exynos_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ehci_init_driver(&exynos_ehci_hc_driver, &exynos_overrides);
+       return platform_driver_register(&exynos_ehci_driver);
+}
+module_init(ehci_exynos_init);
+
+static void __exit ehci_exynos_cleanup(void)
+{
+       platform_driver_unregister(&exynos_ehci_driver);
+}
+module_exit(ehci_exynos_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:exynos-ehci");
+MODULE_AUTHOR("Jingoo Han");
+MODULE_AUTHOR("Joonyoung Shim");
+MODULE_LICENSE("GPL v2");
index f2407b2e8a996210aec7f3e7a442119fd6928924..a06d5012201fe6800c0de5c7363bf8bd1cecb58f 100644 (file)
@@ -57,7 +57,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
        pr_debug("initializing FSL-SOC USB Controller\n");
 
        /* Need platform data for setup */
-       pdata = (struct fsl_usb2_platform_data *)dev_get_platdata(&pdev->dev);
+       pdata = dev_get_platdata(&pdev->dev);
        if (!pdata) {
                dev_err(&pdev->dev,
                        "No platform data for %s.\n", dev_name(&pdev->dev));
@@ -664,7 +664,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
         * generic hardware linkage
         */
        .irq = ehci_irq,
-       .flags = HCD_USB2 | HCD_MEMORY,
+       .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
 
        /*
         * basic lifecycle operations
index 83ab51af250f158e735373760f8c008ce6fe0ad8..b52a66ce92e8592b123239aa24b724dddfd085fa 100644 (file)
@@ -43,7 +43,7 @@ static const struct hc_driver ehci_grlib_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 86ab9fd9fe9e938fc29cadeac72a935e8c26b8ff..e8ba4c44223a5c360ec552cbbb4137ca2b4ca3b2 100644 (file)
@@ -110,6 +110,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
 #include "ehci.h"
 #include "pci-quirks.h"
 
+static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
+               struct ehci_tt *tt);
+
 /*
  * The MosChip MCS9990 controller updates its microframe counter
  * a little before the frame counter, and occasionally we will read
@@ -484,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd)
        INIT_LIST_HEAD(&ehci->intr_qh_list);
        INIT_LIST_HEAD(&ehci->cached_itd_list);
        INIT_LIST_HEAD(&ehci->cached_sitd_list);
+       INIT_LIST_HEAD(&ehci->tt_list);
 
        if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
                /* periodic schedule size can be smaller than default */
@@ -956,6 +960,7 @@ rescan:
                        goto idle_timeout;
 
                /* BUG_ON(!list_empty(&stream->free_list)); */
+               reserve_release_iso_bandwidth(ehci, stream, -1);
                kfree(stream);
                goto done;
        }
@@ -982,6 +987,8 @@ idle_timeout:
                if (qh->clearing_tt)
                        goto idle_timeout;
                if (list_empty (&qh->qtd_list)) {
+                       if (qh->ps.bw_uperiod)
+                               reserve_release_intr_bandwidth(ehci, qh, -1);
                        qh_destroy(ehci, qh);
                        break;
                }
@@ -1022,7 +1029,6 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
         * the toggle bit in the QH.
         */
        if (qh) {
-               usb_settoggle(qh->dev, epnum, is_out, 0);
                if (!list_empty(&qh->qtd_list)) {
                        WARN_ONCE(1, "clear_halt for a busy endpoint\n");
                } else {
@@ -1030,6 +1036,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
                         * while the QH is active.  Unlink it now;
                         * re-linking will call qh_refresh().
                         */
+                       usb_settoggle(qh->ps.udev, epnum, is_out, 0);
                        qh->exception = 1;
                        if (eptype == USB_ENDPOINT_XFER_BULK)
                                start_unlink_async(ehci, qh);
@@ -1048,6 +1055,19 @@ static int ehci_get_frame (struct usb_hcd *hcd)
 
 /*-------------------------------------------------------------------------*/
 
+/* Device addition and removal */
+
+static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+
+       spin_lock_irq(&ehci->lock);
+       drop_tt(udev);
+       spin_unlock_irq(&ehci->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
 #ifdef CONFIG_PM
 
 /* suspend/resume, section 4.3 */
@@ -1075,6 +1095,14 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
        spin_unlock_irq(&ehci->lock);
 
+       synchronize_irq(hcd->irq);
+
+       /* Check for race with a wakeup request */
+       if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+               ehci_resume(hcd, false);
+               return -EBUSY;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(ehci_suspend);
@@ -1158,7 +1186,7 @@ static const struct hc_driver ehci_hc_driver = {
         * generic hardware linkage
         */
        .irq =                  ehci_irq,
-       .flags =                HCD_MEMORY | HCD_USB2,
+       .flags =                HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
@@ -1191,6 +1219,11 @@ static const struct hc_driver ehci_hc_driver = {
        .bus_resume =           ehci_bus_resume,
        .relinquish_port =      ehci_relinquish_port,
        .port_handed_over =     ehci_port_handed_over,
+
+       /*
+        * device support
+        */
+       .free_dev =             ehci_remove_device,
 };
 
 void ehci_init_driver(struct hc_driver *drv,
@@ -1238,11 +1271,6 @@ MODULE_LICENSE ("GPL");
 #define XILINX_OF_PLATFORM_DRIVER      ehci_hcd_xilinx_of_driver
 #endif
 
-#ifdef CONFIG_USB_W90X900_EHCI
-#include "ehci-w90x900.c"
-#define        PLATFORM_DRIVER         ehci_hcd_w90x900_driver
-#endif
-
 #ifdef CONFIG_USB_OCTEON_EHCI
 #include "ehci-octeon.c"
 #define PLATFORM_DRIVER                ehci_octeon_driver
index 52a77734a225fa05a8afc6ede8df5deb4a7089dd..c0fb6a8ae6a3935e367e4a9c386e051acc739338 100644 (file)
@@ -224,11 +224,11 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
                hw->hw_next = EHCI_LIST_END(ehci);
                hw->hw_qtd_next = EHCI_LIST_END(ehci);
                hw->hw_alt_next = EHCI_LIST_END(ehci);
-               hw->hw_token &= ~QTD_STS_ACTIVE;
                ehci->dummy->hw = hw;
 
                for (i = 0; i < ehci->periodic_size; i++)
-                       ehci->periodic[i] = ehci->dummy->qh_dma;
+                       ehci->periodic[i] = cpu_to_hc32(ehci,
+                                       ehci->dummy->qh_dma);
        } else {
                for (i = 0; i < ehci->periodic_size; i++)
                        ehci->periodic[i] = EHCI_LIST_END(ehci);
index 0f717dc688b7276fb75f65a9920bb799facfb164..f341651d6f6ce6e4ae67a2d53ab2cab3aefeb81e 100644 (file)
@@ -42,7 +42,6 @@
 
 static const char hcd_name[] = "ehci-msm";
 static struct hc_driver __read_mostly msm_hc_driver;
-static struct usb_phy *phy;
 
 static int ehci_msm_reset(struct usb_hcd *hcd)
 {
@@ -70,6 +69,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
 {
        struct usb_hcd *hcd;
        struct resource *res;
+       struct usb_phy *phy;
        int ret;
 
        dev_dbg(&pdev->dev, "ehci_msm proble\n");
@@ -108,10 +108,14 @@ static int ehci_msm_probe(struct platform_device *pdev)
         * powering up VBUS, mapping of registers address space and power
         * management.
         */
-       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+       if (pdev->dev.of_node)
+               phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
+       else
+               phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+
        if (IS_ERR(phy)) {
                dev_err(&pdev->dev, "unable to find transceiver\n");
-               ret = -ENODEV;
+               ret = -EPROBE_DEFER;
                goto put_hcd;
        }
 
@@ -121,6 +125,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
                goto put_hcd;
        }
 
+       hcd->phy = phy;
        device_init_wakeup(&pdev->dev, 1);
        /*
         * OTG device parent of HCD takes care of putting
@@ -147,7 +152,7 @@ static int ehci_msm_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        pm_runtime_set_suspended(&pdev->dev);
 
-       otg_set_host(phy->otg, NULL);
+       otg_set_host(hcd->phy->otg, NULL);
 
        /* FIXME: need to call usb_remove_hcd() here? */
 
@@ -186,12 +191,19 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
        .resume          = ehci_msm_pm_resume,
 };
 
+static struct of_device_id msm_ehci_dt_match[] = {
+       { .compatible = "qcom,ehci-host", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, msm_ehci_dt_match);
+
 static struct platform_driver ehci_msm_driver = {
        .probe  = ehci_msm_probe,
        .remove = ehci_msm_remove,
        .driver = {
                   .name = "msm_hsusb_host",
                   .pm = &ehci_msm_dev_pm_ops,
+                  .of_match_table = msm_ehci_dt_match,
        },
 };
 
index 35cdbd88bbbef62a93a3aa869c12dbfda8183bf2..417c10da945078e37ddf20e7be290e996936074e 100644 (file)
@@ -96,7 +96,7 @@ static const struct hc_driver mv_ehci_hc_driver = {
         * generic hardware linkage
         */
        .irq = ehci_irq,
-       .flags = HCD_MEMORY | HCD_USB2,
+       .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 45cc00158412ac8a380cda88a28a4bb7536d62fa..ab0397e4d8f3eadae916d07434f4431f3d59def3 100644 (file)
@@ -51,7 +51,7 @@ static const struct hc_driver ehci_octeon_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 854c2ec7b699d4effe2c9ca6ae4ab264e1a73cd8..3e86bf4371b3901de76ed7e5db8e8a8de5a72cd3 100644 (file)
@@ -58,8 +58,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
        struct pci_dev          *pdev = to_pci_dev(hcd->self.controller);
-       struct pci_dev          *p_smbus;
-       u8                      rev;
        u32                     temp;
        int                     retval;
 
@@ -175,22 +173,12 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                /* SB600 and old version of SB700 have a bug in EHCI controller,
                 * which causes usb devices lose response in some cases.
                 */
-               if ((pdev->device == 0x4386) || (pdev->device == 0x4396)) {
-                       p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
-                                                PCI_DEVICE_ID_ATI_SBX00_SMBUS,
-                                                NULL);
-                       if (!p_smbus)
-                               break;
-                       rev = p_smbus->revision;
-                       if ((pdev->device == 0x4386) || (rev == 0x3a)
-                           || (rev == 0x3b)) {
-                               u8 tmp;
-                               ehci_info(ehci, "applying AMD SB600/SB700 USB "
-                                       "freeze workaround\n");
-                               pci_read_config_byte(pdev, 0x53, &tmp);
-                               pci_write_config_byte(pdev, 0x53, tmp | (1<<3));
-                       }
-                       pci_dev_put(p_smbus);
+               if ((pdev->device == 0x4386 || pdev->device == 0x4396) &&
+                               usb_amd_hang_symptom_quirk()) {
+                       u8 tmp;
+                       ehci_info(ehci, "applying AMD SB600/SB700 USB freeze workaround\n");
+                       pci_read_config_byte(pdev, 0x53, &tmp);
+                       pci_write_config_byte(pdev, 0x53, tmp | (1<<3));
                }
                break;
        case PCI_VENDOR_ID_NETMOS:
index 601e208bd782c07e9d0bb1b60d238ccbb7774758..893b707f0000abf0e323f39b28b6060a3293bcef 100644 (file)
@@ -286,7 +286,7 @@ static const struct hc_driver ehci_msp_hc_driver = {
 #else
        .irq =                  ehci_irq,
 #endif
-       .flags =                HCD_MEMORY | HCD_USB2,
+       .flags =                HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 932293fa32de657de2e36ba091127e50009a6ff7..6cc5567bf9c87faaa8c4a21dcb4202e8e6e7fb94 100644 (file)
@@ -28,7 +28,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index fd983771b02559cb56c6210e6813d9b223a80f7a..8188542ba17ea01214a3ab0f269fe07cb6cb1744 100644 (file)
@@ -71,7 +71,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
        .product_desc           = "PS3 EHCI Host Controller",
        .hcd_priv_size          = sizeof(struct ehci_hcd),
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
        .reset                  = ps3_ehci_hc_reset,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
index a7f776a13eb17133459f23ac584b60287e544197..db05bd8ee9d59e4a739720912ee4ca8faaa61eac 100644 (file)
@@ -105,9 +105,9 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
 
                is_out = qh->is_out;
                epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f;
-               if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
+               if (unlikely(!usb_gettoggle(qh->ps.udev, epnum, is_out))) {
                        hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
-                       usb_settoggle (qh->dev, epnum, is_out, 1);
+                       usb_settoggle(qh->ps.udev, epnum, is_out, 1);
                }
        }
 
@@ -247,8 +247,6 @@ static int qtd_copy_status (
 
 static void
 ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
-__releases(ehci->lock)
-__acquires(ehci->lock)
 {
        if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
                /* ... update hc-wide periodic stats */
@@ -274,11 +272,8 @@ __acquires(ehci->lock)
                urb->actual_length, urb->transfer_buffer_length);
 #endif
 
-       /* complete() can reenter this HCD */
        usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
-       spin_unlock (&ehci->lock);
        usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status);
-       spin_lock (&ehci->lock);
 }
 
 static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
@@ -802,26 +797,35 @@ qh_make (
         * For control/bulk requests, the HC or TT handles these.
         */
        if (type == PIPE_INTERRUPT) {
-               qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
+               unsigned        tmp;
+
+               qh->ps.usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
                                is_input, 0,
                                hb_mult(maxp) * max_packet(maxp)));
-               qh->start = NO_FRAME;
+               qh->ps.phase = NO_FRAME;
 
                if (urb->dev->speed == USB_SPEED_HIGH) {
-                       qh->c_usecs = 0;
+                       qh->ps.c_usecs = 0;
                        qh->gap_uf = 0;
 
-                       qh->period = urb->interval >> 3;
-                       if (qh->period == 0 && urb->interval != 1) {
+                       if (urb->interval > 1 && urb->interval < 8) {
                                /* NOTE interval 2 or 4 uframes could work.
                                 * But interval 1 scheduling is simpler, and
                                 * includes high bandwidth.
                                 */
                                urb->interval = 1;
-                       } else if (qh->period > ehci->periodic_size) {
-                               qh->period = ehci->periodic_size;
-                               urb->interval = qh->period << 3;
+                       } else if (urb->interval > ehci->periodic_size << 3) {
+                               urb->interval = ehci->periodic_size << 3;
                        }
+                       qh->ps.period = urb->interval >> 3;
+
+                       /* period for bandwidth allocation */
+                       tmp = min_t(unsigned, EHCI_BANDWIDTH_SIZE,
+                                       1 << (urb->ep->desc.bInterval - 1));
+
+                       /* Allow urb->interval to override */
+                       qh->ps.bw_uperiod = min_t(unsigned, tmp, urb->interval);
+                       qh->ps.bw_period = qh->ps.bw_uperiod >> 3;
                } else {
                        int             think_time;
 
@@ -831,27 +835,35 @@ qh_make (
 
                        /* FIXME this just approximates SPLIT/CSPLIT times */
                        if (is_input) {         // SPLIT, gap, CSPLIT+DATA
-                               qh->c_usecs = qh->usecs + HS_USECS (0);
-                               qh->usecs = HS_USECS (1);
+                               qh->ps.c_usecs = qh->ps.usecs + HS_USECS(0);
+                               qh->ps.usecs = HS_USECS(1);
                        } else {                // SPLIT+DATA, gap, CSPLIT
-                               qh->usecs += HS_USECS (1);
-                               qh->c_usecs = HS_USECS (0);
+                               qh->ps.usecs += HS_USECS(1);
+                               qh->ps.c_usecs = HS_USECS(0);
                        }
 
                        think_time = tt ? tt->think_time : 0;
-                       qh->tt_usecs = NS_TO_US (think_time +
+                       qh->ps.tt_usecs = NS_TO_US(think_time +
                                        usb_calc_bus_time (urb->dev->speed,
                                        is_input, 0, max_packet (maxp)));
-                       qh->period = urb->interval;
-                       if (qh->period > ehci->periodic_size) {
-                               qh->period = ehci->periodic_size;
-                               urb->interval = qh->period;
-                       }
+                       if (urb->interval > ehci->periodic_size)
+                               urb->interval = ehci->periodic_size;
+                       qh->ps.period = urb->interval;
+
+                       /* period for bandwidth allocation */
+                       tmp = min_t(unsigned, EHCI_BANDWIDTH_FRAMES,
+                                       urb->ep->desc.bInterval);
+                       tmp = rounddown_pow_of_two(tmp);
+
+                       /* Allow urb->interval to override */
+                       qh->ps.bw_period = min_t(unsigned, tmp, urb->interval);
+                       qh->ps.bw_uperiod = qh->ps.bw_period << 3;
                }
        }
 
        /* support for tt scheduling, and access to toggles */
-       qh->dev = urb->dev;
+       qh->ps.udev = urb->dev;
+       qh->ps.ep = urb->ep;
 
        /* using TT? */
        switch (urb->dev->speed) {
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
deleted file mode 100644 (file)
index 7c3de95..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * SAMSUNG S5P USB HOST EHCI Controller
- *
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Author: Jingoo Han <jg1.han@samsung.com>
- * Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/usb-ehci-s5p.h>
-#include <linux/usb/phy.h>
-#include <linux/usb/samsung_usb_phy.h>
-#include <linux/usb.h>
-#include <linux/usb/hcd.h>
-#include <linux/usb/otg.h>
-
-#include "ehci.h"
-
-#define DRIVER_DESC "EHCI s5p driver"
-
-#define EHCI_INSNREG00(base)                   (base + 0x90)
-#define EHCI_INSNREG00_ENA_INCR16              (0x1 << 25)
-#define EHCI_INSNREG00_ENA_INCR8               (0x1 << 24)
-#define EHCI_INSNREG00_ENA_INCR4               (0x1 << 23)
-#define EHCI_INSNREG00_ENA_INCRX_ALIGN         (0x1 << 22)
-#define EHCI_INSNREG00_ENABLE_DMA_BURST        \
-       (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \
-        EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
-
-static const char hcd_name[] = "ehci-s5p";
-static struct hc_driver __read_mostly s5p_ehci_hc_driver;
-
-struct s5p_ehci_hcd {
-       struct clk *clk;
-       struct usb_phy *phy;
-       struct usb_otg *otg;
-       struct s5p_ehci_platdata *pdata;
-};
-
-static struct s5p_ehci_platdata empty_platdata;
-
-#define to_s5p_ehci(hcd)      (struct s5p_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
-
-static void s5p_setup_vbus_gpio(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       int err;
-       int gpio;
-
-       if (!dev->of_node)
-               return;
-
-       gpio = of_get_named_gpio(dev->of_node, "samsung,vbus-gpio", 0);
-       if (!gpio_is_valid(gpio))
-               return;
-
-       err = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
-                                   "ehci_vbus_gpio");
-       if (err)
-               dev_err(dev, "can't request ehci vbus gpio %d", gpio);
-}
-
-static int s5p_ehci_probe(struct platform_device *pdev)
-{
-       struct s5p_ehci_platdata *pdata = dev_get_platdata(&pdev->dev);
-       struct s5p_ehci_hcd *s5p_ehci;
-       struct usb_hcd *hcd;
-       struct ehci_hcd *ehci;
-       struct resource *res;
-       struct usb_phy *phy;
-       int irq;
-       int err;
-
-       /*
-        * Right now device-tree probed devices don't get dma_mask set.
-        * Since shared usb code relies on it, set it here for now.
-        * Once we move to full device tree support this will vanish off.
-        */
-       if (!pdev->dev.dma_mask)
-               pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
-       if (!pdev->dev.coherent_dma_mask)
-               pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
-
-       s5p_setup_vbus_gpio(pdev);
-
-       hcd = usb_create_hcd(&s5p_ehci_hc_driver,
-                            &pdev->dev, dev_name(&pdev->dev));
-       if (!hcd) {
-               dev_err(&pdev->dev, "Unable to create HCD\n");
-               return -ENOMEM;
-       }
-       s5p_ehci = to_s5p_ehci(hcd);
-
-       if (of_device_is_compatible(pdev->dev.of_node,
-                                       "samsung,exynos5440-ehci")) {
-               s5p_ehci->pdata = &empty_platdata;
-               goto skip_phy;
-       }
-
-       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
-       if (IS_ERR(phy)) {
-               /* Fallback to pdata */
-               if (!pdata) {
-                       usb_put_hcd(hcd);
-                       dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
-                       return -EPROBE_DEFER;
-               } else {
-                       s5p_ehci->pdata = pdata;
-               }
-       } else {
-               s5p_ehci->phy = phy;
-               s5p_ehci->otg = phy->otg;
-       }
-
-skip_phy:
-
-       s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
-
-       if (IS_ERR(s5p_ehci->clk)) {
-               dev_err(&pdev->dev, "Failed to get usbhost clock\n");
-               err = PTR_ERR(s5p_ehci->clk);
-               goto fail_clk;
-       }
-
-       err = clk_prepare_enable(s5p_ehci->clk);
-       if (err)
-               goto fail_clk;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "Failed to get I/O memory\n");
-               err = -ENXIO;
-               goto fail_io;
-       }
-
-       hcd->rsrc_start = res->start;
-       hcd->rsrc_len = resource_size(res);
-       hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
-       if (!hcd->regs) {
-               dev_err(&pdev->dev, "Failed to remap I/O memory\n");
-               err = -ENOMEM;
-               goto fail_io;
-       }
-
-       irq = platform_get_irq(pdev, 0);
-       if (!irq) {
-               dev_err(&pdev->dev, "Failed to get IRQ\n");
-               err = -ENODEV;
-               goto fail_io;
-       }
-
-       if (s5p_ehci->otg)
-               s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
-
-       if (s5p_ehci->phy)
-               usb_phy_init(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_init)
-               s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
-
-       ehci = hcd_to_ehci(hcd);
-       ehci->caps = hcd->regs;
-
-       /* DMA burst Enable */
-       writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
-
-       err = usb_add_hcd(hcd, irq, IRQF_SHARED);
-       if (err) {
-               dev_err(&pdev->dev, "Failed to add USB HCD\n");
-               goto fail_add_hcd;
-       }
-
-       platform_set_drvdata(pdev, hcd);
-
-       return 0;
-
-fail_add_hcd:
-       if (s5p_ehci->phy)
-               usb_phy_shutdown(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_exit)
-               s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
-fail_io:
-       clk_disable_unprepare(s5p_ehci->clk);
-fail_clk:
-       usb_put_hcd(hcd);
-       return err;
-}
-
-static int s5p_ehci_remove(struct platform_device *pdev)
-{
-       struct usb_hcd *hcd = platform_get_drvdata(pdev);
-       struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
-
-       usb_remove_hcd(hcd);
-
-       if (s5p_ehci->otg)
-               s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
-
-       if (s5p_ehci->phy)
-               usb_phy_shutdown(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_exit)
-               s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
-
-       clk_disable_unprepare(s5p_ehci->clk);
-
-       usb_put_hcd(hcd);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int s5p_ehci_suspend(struct device *dev)
-{
-       struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
-       struct platform_device *pdev = to_platform_device(dev);
-
-       bool do_wakeup = device_may_wakeup(dev);
-       int rc;
-
-       rc = ehci_suspend(hcd, do_wakeup);
-
-       if (s5p_ehci->otg)
-               s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
-
-       if (s5p_ehci->phy)
-               usb_phy_shutdown(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_exit)
-               s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
-
-       clk_disable_unprepare(s5p_ehci->clk);
-
-       return rc;
-}
-
-static int s5p_ehci_resume(struct device *dev)
-{
-       struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct  s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
-       struct platform_device *pdev = to_platform_device(dev);
-
-       clk_prepare_enable(s5p_ehci->clk);
-
-       if (s5p_ehci->otg)
-               s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
-
-       if (s5p_ehci->phy)
-               usb_phy_init(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_init)
-               s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
-
-       /* DMA burst Enable */
-       writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
-
-       ehci_resume(hcd, false);
-       return 0;
-}
-#else
-#define s5p_ehci_suspend       NULL
-#define s5p_ehci_resume                NULL
-#endif
-
-static const struct dev_pm_ops s5p_ehci_pm_ops = {
-       .suspend        = s5p_ehci_suspend,
-       .resume         = s5p_ehci_resume,
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id exynos_ehci_match[] = {
-       { .compatible = "samsung,exynos4210-ehci" },
-       { .compatible = "samsung,exynos5440-ehci" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, exynos_ehci_match);
-#endif
-
-static struct platform_driver s5p_ehci_driver = {
-       .probe          = s5p_ehci_probe,
-       .remove         = s5p_ehci_remove,
-       .shutdown       = usb_hcd_platform_shutdown,
-       .driver = {
-               .name   = "s5p-ehci",
-               .owner  = THIS_MODULE,
-               .pm     = &s5p_ehci_pm_ops,
-               .of_match_table = of_match_ptr(exynos_ehci_match),
-       }
-};
-static const struct ehci_driver_overrides s5p_overrides __initdata = {
-       .extra_priv_size = sizeof(struct s5p_ehci_hcd),
-};
-
-static int __init ehci_s5p_init(void)
-{
-       if (usb_disabled())
-               return -ENODEV;
-
-       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
-       ehci_init_driver(&s5p_ehci_hc_driver, &s5p_overrides);
-       return platform_driver_register(&s5p_ehci_driver);
-}
-module_init(ehci_s5p_init);
-
-static void __exit ehci_s5p_cleanup(void)
-{
-       platform_driver_unregister(&s5p_ehci_driver);
-}
-module_exit(ehci_s5p_cleanup);
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_ALIAS("platform:s5p-ehci");
-MODULE_AUTHOR("Jingoo Han");
-MODULE_AUTHOR("Joonyoung Shim");
-MODULE_LICENSE("GPL v2");
index 85dd24ed97a6d6cf6700405797da29d313e76958..e113fd73aeae7148b0cbcd0d424aebb16d84a694 100644 (file)
@@ -103,83 +103,210 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
                *hw_p = *shadow_next_periodic(ehci, &here,
                                Q_NEXT_TYPE(ehci, *hw_p));
        else
-               *hw_p = ehci->dummy->qh_dma;
+               *hw_p = cpu_to_hc32(ehci, ehci->dummy->qh_dma);
 }
 
-/* how many of the uframe's 125 usecs are allocated? */
-static unsigned short
-periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
+/*-------------------------------------------------------------------------*/
+
+/* Bandwidth and TT management */
+
+/* Find the TT data structure for this device; create it if necessary */
+static struct ehci_tt *find_tt(struct usb_device *udev)
 {
-       __hc32                  *hw_p = &ehci->periodic [frame];
-       union ehci_shadow       *q = &ehci->pshadow [frame];
-       unsigned                usecs = 0;
-       struct ehci_qh_hw       *hw;
-
-       while (q->ptr) {
-               switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
-               case Q_TYPE_QH:
-                       hw = q->qh->hw;
-                       /* is it in the S-mask? */
-                       if (hw->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
-                               usecs += q->qh->usecs;
-                       /* ... or C-mask? */
-                       if (hw->hw_info2 & cpu_to_hc32(ehci,
-                                       1 << (8 + uframe)))
-                               usecs += q->qh->c_usecs;
-                       hw_p = &hw->hw_next;
-                       q = &q->qh->qh_next;
-                       break;
-               // case Q_TYPE_FSTN:
-               default:
-                       /* for "save place" FSTNs, count the relevant INTR
-                        * bandwidth from the previous frame
-                        */
-                       if (q->fstn->hw_prev != EHCI_LIST_END(ehci)) {
-                               ehci_dbg (ehci, "ignoring FSTN cost ...\n");
-                       }
-                       hw_p = &q->fstn->hw_next;
-                       q = &q->fstn->fstn_next;
-                       break;
-               case Q_TYPE_ITD:
-                       if (q->itd->hw_transaction[uframe])
-                               usecs += q->itd->stream->usecs;
-                       hw_p = &q->itd->hw_next;
-                       q = &q->itd->itd_next;
-                       break;
-               case Q_TYPE_SITD:
-                       /* is it in the S-mask?  (count SPLIT, DATA) */
-                       if (q->sitd->hw_uframe & cpu_to_hc32(ehci,
-                                       1 << uframe)) {
-                               if (q->sitd->hw_fullspeed_ep &
-                                               cpu_to_hc32(ehci, 1<<31))
-                                       usecs += q->sitd->stream->usecs;
-                               else    /* worst case for OUT start-split */
-                                       usecs += HS_USECS_ISO (188);
-                       }
+       struct usb_tt           *utt = udev->tt;
+       struct ehci_tt          *tt, **tt_index, **ptt;
+       unsigned                port;
+       bool                    allocated_index = false;
+
+       if (!utt)
+               return NULL;            /* Not below a TT */
+
+       /*
+        * Find/create our data structure.
+        * For hubs with a single TT, we get it directly.
+        * For hubs with multiple TTs, there's an extra level of pointers.
+        */
+       tt_index = NULL;
+       if (utt->multi) {
+               tt_index = utt->hcpriv;
+               if (!tt_index) {                /* Create the index array */
+                       tt_index = kzalloc(utt->hub->maxchild *
+                                       sizeof(*tt_index), GFP_ATOMIC);
+                       if (!tt_index)
+                               return ERR_PTR(-ENOMEM);
+                       utt->hcpriv = tt_index;
+                       allocated_index = true;
+               }
+               port = udev->ttport - 1;
+               ptt = &tt_index[port];
+       } else {
+               port = 0;
+               ptt = (struct ehci_tt **) &utt->hcpriv;
+       }
+
+       tt = *ptt;
+       if (!tt) {                              /* Create the ehci_tt */
+               struct ehci_hcd         *ehci =
+                               hcd_to_ehci(bus_to_hcd(udev->bus));
 
-                       /* ... C-mask?  (count CSPLIT, DATA) */
-                       if (q->sitd->hw_uframe &
-                                       cpu_to_hc32(ehci, 1 << (8 + uframe))) {
-                               /* worst case for IN complete-split */
-                               usecs += q->sitd->stream->c_usecs;
+               tt = kzalloc(sizeof(*tt), GFP_ATOMIC);
+               if (!tt) {
+                       if (allocated_index) {
+                               utt->hcpriv = NULL;
+                               kfree(tt_index);
                        }
+                       return ERR_PTR(-ENOMEM);
+               }
+               list_add_tail(&tt->tt_list, &ehci->tt_list);
+               INIT_LIST_HEAD(&tt->ps_list);
+               tt->usb_tt = utt;
+               tt->tt_port = port;
+               *ptt = tt;
+       }
 
-                       hw_p = &q->sitd->hw_next;
-                       q = &q->sitd->sitd_next;
-                       break;
+       return tt;
+}
+
+/* Release the TT above udev, if it's not in use */
+static void drop_tt(struct usb_device *udev)
+{
+       struct usb_tt           *utt = udev->tt;
+       struct ehci_tt          *tt, **tt_index, **ptt;
+       int                     cnt, i;
+
+       if (!utt || !utt->hcpriv)
+               return;         /* Not below a TT, or never allocated */
+
+       cnt = 0;
+       if (utt->multi) {
+               tt_index = utt->hcpriv;
+               ptt = &tt_index[udev->ttport - 1];
+
+               /* How many entries are left in tt_index? */
+               for (i = 0; i < utt->hub->maxchild; ++i)
+                       cnt += !!tt_index[i];
+       } else {
+               tt_index = NULL;
+               ptt = (struct ehci_tt **) &utt->hcpriv;
+       }
+
+       tt = *ptt;
+       if (!tt || !list_empty(&tt->ps_list))
+               return;         /* never allocated, or still in use */
+
+       list_del(&tt->tt_list);
+       *ptt = NULL;
+       kfree(tt);
+       if (cnt == 1) {
+               utt->hcpriv = NULL;
+               kfree(tt_index);
+       }
+}
+
+static void bandwidth_dbg(struct ehci_hcd *ehci, int sign, char *type,
+               struct ehci_per_sched *ps)
+{
+       dev_dbg(&ps->udev->dev,
+                       "ep %02x: %s %s @ %u+%u (%u.%u+%u) [%u/%u us] mask %04x\n",
+                       ps->ep->desc.bEndpointAddress,
+                       (sign >= 0 ? "reserve" : "release"), type,
+                       (ps->bw_phase << 3) + ps->phase_uf, ps->bw_uperiod,
+                       ps->phase, ps->phase_uf, ps->period,
+                       ps->usecs, ps->c_usecs, ps->cs_mask);
+}
+
+static void reserve_release_intr_bandwidth(struct ehci_hcd *ehci,
+               struct ehci_qh *qh, int sign)
+{
+       unsigned                start_uf;
+       unsigned                i, j, m;
+       int                     usecs = qh->ps.usecs;
+       int                     c_usecs = qh->ps.c_usecs;
+       int                     tt_usecs = qh->ps.tt_usecs;
+       struct ehci_tt          *tt;
+
+       if (qh->ps.phase == NO_FRAME)   /* Bandwidth wasn't reserved */
+               return;
+       start_uf = qh->ps.bw_phase << 3;
+
+       bandwidth_dbg(ehci, sign, "intr", &qh->ps);
+
+       if (sign < 0) {         /* Release bandwidth */
+               usecs = -usecs;
+               c_usecs = -c_usecs;
+               tt_usecs = -tt_usecs;
+       }
+
+       /* Entire transaction (high speed) or start-split (full/low speed) */
+       for (i = start_uf + qh->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE;
+                       i += qh->ps.bw_uperiod)
+               ehci->bandwidth[i] += usecs;
+
+       /* Complete-split (full/low speed) */
+       if (qh->ps.c_usecs) {
+               /* NOTE: adjustments needed for FSTN */
+               for (i = start_uf; i < EHCI_BANDWIDTH_SIZE;
+                               i += qh->ps.bw_uperiod) {
+                       for ((j = 2, m = 1 << (j+8)); j < 8; (++j, m <<= 1)) {
+                               if (qh->ps.cs_mask & m)
+                                       ehci->bandwidth[i+j] += c_usecs;
+                       }
                }
        }
-#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
-       if (usecs > ehci->uframe_periodic_max)
-               ehci_err (ehci, "uframe %d sched overrun: %d usecs\n",
-                       frame * 8 + uframe, usecs);
-#endif
-       return usecs;
+
+       /* FS/LS bus bandwidth */
+       if (tt_usecs) {
+               tt = find_tt(qh->ps.udev);
+               if (sign > 0)
+                       list_add_tail(&qh->ps.ps_list, &tt->ps_list);
+               else
+                       list_del(&qh->ps.ps_list);
+
+               for (i = start_uf >> 3; i < EHCI_BANDWIDTH_FRAMES;
+                               i += qh->ps.bw_period)
+                       tt->bandwidth[i] += tt_usecs;
+       }
 }
 
 /*-------------------------------------------------------------------------*/
 
-static int same_tt (struct usb_device *dev1, struct usb_device *dev2)
+static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
+               struct ehci_tt *tt)
+{
+       struct ehci_per_sched   *ps;
+       unsigned                uframe, uf, x;
+       u8                      *budget_line;
+
+       if (!tt)
+               return;
+       memset(budget_table, 0, EHCI_BANDWIDTH_SIZE);
+
+       /* Add up the contributions from all the endpoints using this TT */
+       list_for_each_entry(ps, &tt->ps_list, ps_list) {
+               for (uframe = ps->bw_phase << 3; uframe < EHCI_BANDWIDTH_SIZE;
+                               uframe += ps->bw_uperiod) {
+                       budget_line = &budget_table[uframe];
+                       x = ps->tt_usecs;
+
+                       /* propagate the time forward */
+                       for (uf = ps->phase_uf; uf < 8; ++uf) {
+                               x += budget_line[uf];
+
+                               /* Each microframe lasts 125 us */
+                               if (x <= 125) {
+                                       budget_line[uf] = x;
+                                       break;
+                               } else {
+                                       budget_line[uf] = 125;
+                                       x -= 125;
+                               }
+                       }
+               }
+       }
+}
+
+static int __maybe_unused same_tt(struct usb_device *dev1,
+               struct usb_device *dev2)
 {
        if (!dev1->tt || !dev2->tt)
                return 0;
@@ -227,68 +354,6 @@ static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8])
        }
 }
 
-/* How many of the tt's periodic downstream 1000 usecs are allocated?
- *
- * While this measures the bandwidth in terms of usecs/uframe,
- * the low/fullspeed bus has no notion of uframes, so any particular
- * low/fullspeed transfer can "carry over" from one uframe to the next,
- * since the TT just performs downstream transfers in sequence.
- *
- * For example two separate 100 usec transfers can start in the same uframe,
- * and the second one would "carry over" 75 usecs into the next uframe.
- */
-static void
-periodic_tt_usecs (
-       struct ehci_hcd *ehci,
-       struct usb_device *dev,
-       unsigned frame,
-       unsigned short tt_usecs[8]
-)
-{
-       __hc32                  *hw_p = &ehci->periodic [frame];
-       union ehci_shadow       *q = &ehci->pshadow [frame];
-       unsigned char           uf;
-
-       memset(tt_usecs, 0, 16);
-
-       while (q->ptr) {
-               switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
-               case Q_TYPE_ITD:
-                       hw_p = &q->itd->hw_next;
-                       q = &q->itd->itd_next;
-                       continue;
-               case Q_TYPE_QH:
-                       if (same_tt(dev, q->qh->dev)) {
-                               uf = tt_start_uframe(ehci, q->qh->hw->hw_info2);
-                               tt_usecs[uf] += q->qh->tt_usecs;
-                       }
-                       hw_p = &q->qh->hw->hw_next;
-                       q = &q->qh->qh_next;
-                       continue;
-               case Q_TYPE_SITD:
-                       if (same_tt(dev, q->sitd->urb->dev)) {
-                               uf = tt_start_uframe(ehci, q->sitd->hw_uframe);
-                               tt_usecs[uf] += q->sitd->stream->tt_usecs;
-                       }
-                       hw_p = &q->sitd->hw_next;
-                       q = &q->sitd->sitd_next;
-                       continue;
-               // case Q_TYPE_FSTN:
-               default:
-                       ehci_dbg(ehci, "ignoring periodic frame %d FSTN\n",
-                                       frame);
-                       hw_p = &q->fstn->hw_next;
-                       q = &q->fstn->fstn_next;
-               }
-       }
-
-       carryover_tt_bandwidth(tt_usecs);
-
-       if (max_tt_usecs[7] < tt_usecs[7])
-               ehci_err(ehci, "frame %d tt sched overrun: %d usecs\n",
-                       frame, tt_usecs[7] - max_tt_usecs[7]);
-}
-
 /*
  * Return true if the device's tt's downstream bus is available for a
  * periodic transfer of the specified length (usecs), starting at the
@@ -312,20 +377,29 @@ periodic_tt_usecs (
  */
 static int tt_available (
        struct ehci_hcd         *ehci,
-       unsigned                period,
-       struct usb_device       *dev,
+       struct ehci_per_sched   *ps,
+       struct ehci_tt          *tt,
        unsigned                frame,
-       unsigned                uframe,
-       u16                     usecs
+       unsigned                uframe
 )
 {
+       unsigned                period = ps->bw_period;
+       unsigned                usecs = ps->tt_usecs;
+
        if ((period == 0) || (uframe >= 7))     /* error */
                return 0;
 
-       for (; frame < ehci->periodic_size; frame += period) {
-               unsigned short tt_usecs[8];
+       for (frame &= period - 1; frame < EHCI_BANDWIDTH_FRAMES;
+                       frame += period) {
+               unsigned        i, uf;
+               unsigned short  tt_usecs[8];
 
-               periodic_tt_usecs (ehci, dev, frame, tt_usecs);
+               if (tt->bandwidth[frame] + usecs > 900)
+                       return 0;
+
+               uf = frame << 3;
+               for (i = 0; i < 8; (++i, ++uf))
+                       tt_usecs[i] = ehci->tt_budget[uf];
 
                if (max_tt_usecs[uframe] <= tt_usecs[uframe])
                        return 0;
@@ -337,7 +411,7 @@ static int tt_available (
                 */
                if (125 < usecs) {
                        int ufs = (usecs / 125);
-                       int i;
+
                        for (i = uframe; i < (uframe + ufs) && i < 8; i++)
                                if (0 < tt_usecs[i])
                                        return 0;
@@ -391,7 +465,7 @@ static int tt_no_collision (
                                continue;
                        case Q_TYPE_QH:
                                hw = here.qh->hw;
-                               if (same_tt (dev, here.qh->dev)) {
+                               if (same_tt(dev, here.qh->ps.udev)) {
                                        u32             mask;
 
                                        mask = hc32_to_cpu(ehci,
@@ -471,19 +545,19 @@ static void disable_periodic(struct ehci_hcd *ehci)
 static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
        unsigned        i;
-       unsigned        period = qh->period;
+       unsigned        period = qh->ps.period;
 
-       dev_dbg (&qh->dev->dev,
+       dev_dbg(&qh->ps.udev->dev,
                "link qh%d-%04x/%p start %d [%d/%d us]\n",
                period, hc32_to_cpup(ehci, &qh->hw->hw_info2)
                        & (QH_CMASK | QH_SMASK),
-               qh, qh->start, qh->usecs, qh->c_usecs);
+               qh, qh->ps.phase, qh->ps.usecs, qh->ps.c_usecs);
 
        /* high bandwidth, or otherwise every microframe */
        if (period == 0)
                period = 1;
 
-       for (i = qh->start; i < ehci->periodic_size; i += period) {
+       for (i = qh->ps.phase; i < ehci->periodic_size; i += period) {
                union ehci_shadow       *prev = &ehci->pshadow[i];
                __hc32                  *hw_p = &ehci->periodic[i];
                union ehci_shadow       here = *prev;
@@ -503,7 +577,7 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
                 * enables sharing interior tree nodes
                 */
                while (here.ptr && qh != here.qh) {
-                       if (qh->period > here.qh->period)
+                       if (qh->ps.period > here.qh->ps.period)
                                break;
                        prev = &here.qh->qh_next;
                        hw_p = &here.qh->hw->hw_next;
@@ -523,10 +597,10 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
        qh->xacterrs = 0;
        qh->exception = 0;
 
-       /* update per-qh bandwidth for usbfs */
-       ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period
-               ? ((qh->usecs + qh->c_usecs) / qh->period)
-               : (qh->usecs * 8);
+       /* update per-qh bandwidth for debugfs */
+       ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->ps.bw_period
+               ? ((qh->ps.usecs + qh->ps.c_usecs) / qh->ps.bw_period)
+               : (qh->ps.usecs * 8);
 
        list_add(&qh->intr_node, &ehci->intr_qh_list);
 
@@ -556,22 +630,21 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
         */
 
        /* high bandwidth, or otherwise part of every microframe */
-       if ((period = qh->period) == 0)
-               period = 1;
+       period = qh->ps.period ? : 1;
 
-       for (i = qh->start; i < ehci->periodic_size; i += period)
+       for (i = qh->ps.phase; i < ehci->periodic_size; i += period)
                periodic_unlink (ehci, i, qh);
 
-       /* update per-qh bandwidth for usbfs */
-       ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period
-               ? ((qh->usecs + qh->c_usecs) / qh->period)
-               : (qh->usecs * 8);
+       /* update per-qh bandwidth for debugfs */
+       ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->ps.bw_period
+               ? ((qh->ps.usecs + qh->ps.c_usecs) / qh->ps.bw_period)
+               : (qh->ps.usecs * 8);
 
-       dev_dbg (&qh->dev->dev,
+       dev_dbg(&qh->ps.udev->dev,
                "unlink qh%d-%04x/%p start %d [%d/%d us]\n",
-               qh->period,
+               qh->ps.period,
                hc32_to_cpup(ehci, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK),
-               qh, qh->start, qh->usecs, qh->c_usecs);
+               qh, qh->ps.phase, qh->ps.usecs, qh->ps.c_usecs);
 
        /* qh->qh_next still "live" to HC */
        qh->qh_state = QH_STATE_UNLINK;
@@ -694,11 +767,9 @@ static int check_period (
        struct ehci_hcd *ehci,
        unsigned        frame,
        unsigned        uframe,
-       unsigned        period,
+       unsigned        uperiod,
        unsigned        usecs
 ) {
-       int             claimed;
-
        /* complete split running into next frame?
         * given FSTN support, we could sometimes check...
         */
@@ -708,25 +779,10 @@ static int check_period (
        /* convert "usecs we need" to "max already claimed" */
        usecs = ehci->uframe_periodic_max - usecs;
 
-       /* we "know" 2 and 4 uframe intervals were rejected; so
-        * for period 0, check _every_ microframe in the schedule.
-        */
-       if (unlikely (period == 0)) {
-               do {
-                       for (uframe = 0; uframe < 7; uframe++) {
-                               claimed = periodic_usecs (ehci, frame, uframe);
-                               if (claimed > usecs)
-                                       return 0;
-                       }
-               } while ((frame += 1) < ehci->periodic_size);
-
-       /* just check the specified uframe, at that period */
-       } else {
-               do {
-                       claimed = periodic_usecs (ehci, frame, uframe);
-                       if (claimed > usecs)
-                               return 0;
-               } while ((frame += period) < ehci->periodic_size);
+       for (uframe += frame << 3; uframe < EHCI_BANDWIDTH_SIZE;
+                       uframe += uperiod) {
+               if (ehci->bandwidth[uframe] > usecs)
+                       return 0;
        }
 
        // success!
@@ -737,40 +793,40 @@ static int check_intr_schedule (
        struct ehci_hcd         *ehci,
        unsigned                frame,
        unsigned                uframe,
-       const struct ehci_qh    *qh,
-       __hc32                  *c_maskp
+       struct ehci_qh          *qh,
+       unsigned                *c_maskp,
+       struct ehci_tt          *tt
 )
 {
        int             retval = -ENOSPC;
        u8              mask = 0;
 
-       if (qh->c_usecs && uframe >= 6)         /* FSTN territory? */
+       if (qh->ps.c_usecs && uframe >= 6)      /* FSTN territory? */
                goto done;
 
-       if (!check_period (ehci, frame, uframe, qh->period, qh->usecs))
+       if (!check_period(ehci, frame, uframe, qh->ps.bw_uperiod, qh->ps.usecs))
                goto done;
-       if (!qh->c_usecs) {
+       if (!qh->ps.c_usecs) {
                retval = 0;
                *c_maskp = 0;
                goto done;
        }
 
 #ifdef CONFIG_USB_EHCI_TT_NEWSCHED
-       if (tt_available (ehci, qh->period, qh->dev, frame, uframe,
-                               qh->tt_usecs)) {
+       if (tt_available(ehci, &qh->ps, tt, frame, uframe)) {
                unsigned i;
 
                /* TODO : this may need FSTN for SSPLIT in uframe 5. */
-               for (i=uframe+1; i<8 && i<uframe+4; i++)
-                       if (!check_period (ehci, frame, i,
-                                               qh->period, qh->c_usecs))
+               for (i = uframe+2; i < 8 && i <= uframe+4; i++)
+                       if (!check_period(ehci, frame, i,
+                                       qh->ps.bw_uperiod, qh->ps.c_usecs))
                                goto done;
                        else
                                mask |= 1 << i;
 
                retval = 0;
 
-               *c_maskp = cpu_to_hc32(ehci, mask << 8);
+               *c_maskp = mask;
        }
 #else
        /* Make sure this tt's buffer is also available for CSPLITs.
@@ -781,15 +837,15 @@ static int check_intr_schedule (
         * one smart pass...
         */
        mask = 0x03 << (uframe + qh->gap_uf);
-       *c_maskp = cpu_to_hc32(ehci, mask << 8);
+       *c_maskp = mask;
 
        mask |= 1 << uframe;
-       if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) {
-               if (!check_period (ehci, frame, uframe + qh->gap_uf + 1,
-                                       qh->period, qh->c_usecs))
+       if (tt_no_collision(ehci, qh->ps.bw_period, qh->ps.udev, frame, mask)) {
+               if (!check_period(ehci, frame, uframe + qh->gap_uf + 1,
+                               qh->ps.bw_uperiod, qh->ps.c_usecs))
                        goto done;
-               if (!check_period (ehci, frame, uframe + qh->gap_uf,
-                                       qh->period, qh->c_usecs))
+               if (!check_period(ehci, frame, uframe + qh->gap_uf,
+                               qh->ps.bw_uperiod, qh->ps.c_usecs))
                        goto done;
                retval = 0;
        }
@@ -803,62 +859,67 @@ done:
  */
 static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       int             status;
+       int             status = 0;
        unsigned        uframe;
-       __hc32          c_mask;
-       unsigned        frame;          /* 0..(qh->period - 1), or NO_FRAME */
+       unsigned        c_mask;
        struct ehci_qh_hw       *hw = qh->hw;
+       struct ehci_tt          *tt;
 
        hw->hw_next = EHCI_LIST_END(ehci);
-       frame = qh->start;
 
        /* reuse the previous schedule slots, if we can */
-       if (frame < qh->period) {
-               uframe = ffs(hc32_to_cpup(ehci, &hw->hw_info2) & QH_SMASK);
-               status = check_intr_schedule (ehci, frame, --uframe,
-                               qh, &c_mask);
-       } else {
-               uframe = 0;
-               c_mask = 0;
-               status = -ENOSPC;
+       if (qh->ps.phase != NO_FRAME) {
+               ehci_dbg(ehci, "reused qh %p schedule\n", qh);
+               return 0;
+       }
+
+       uframe = 0;
+       c_mask = 0;
+       tt = find_tt(qh->ps.udev);
+       if (IS_ERR(tt)) {
+               status = PTR_ERR(tt);
+               goto done;
        }
+       compute_tt_budget(ehci->tt_budget, tt);
 
        /* else scan the schedule to find a group of slots such that all
         * uframes have enough periodic bandwidth available.
         */
-       if (status) {
-               /* "normal" case, uframing flexible except with splits */
-               if (qh->period) {
-                       int             i;
-
-                       for (i = qh->period; status && i > 0; --i) {
-                               frame = ++ehci->random_frame % qh->period;
-                               for (uframe = 0; uframe < 8; uframe++) {
-                                       status = check_intr_schedule (ehci,
-                                                       frame, uframe, qh,
-                                                       &c_mask);
-                                       if (status == 0)
-                                               break;
-                               }
+       /* "normal" case, uframing flexible except with splits */
+       if (qh->ps.bw_period) {
+               int             i;
+               unsigned        frame;
+
+               for (i = qh->ps.bw_period; i > 0; --i) {
+                       frame = ++ehci->random_frame & (qh->ps.bw_period - 1);
+                       for (uframe = 0; uframe < 8; uframe++) {
+                               status = check_intr_schedule(ehci,
+                                               frame, uframe, qh, &c_mask, tt);
+                               if (status == 0)
+                                       goto got_it;
                        }
-
-               /* qh->period == 0 means every uframe */
-               } else {
-                       frame = 0;
-                       status = check_intr_schedule (ehci, 0, 0, qh, &c_mask);
                }
-               if (status)
-                       goto done;
-               qh->start = frame;
 
-               /* reset S-frame and (maybe) C-frame masks */
-               hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
-               hw->hw_info2 |= qh->period
-                       ? cpu_to_hc32(ehci, 1 << uframe)
-                       : cpu_to_hc32(ehci, QH_SMASK);
-               hw->hw_info2 |= c_mask;
-       } else
-               ehci_dbg (ehci, "reused qh %p schedule\n", qh);
+       /* qh->ps.bw_period == 0 means every uframe */
+       } else {
+               status = check_intr_schedule(ehci, 0, 0, qh, &c_mask, tt);
+       }
+       if (status)
+               goto done;
+
+ got_it:
+       qh->ps.phase = (qh->ps.period ? ehci->random_frame &
+                       (qh->ps.period - 1) : 0);
+       qh->ps.bw_phase = qh->ps.phase & (qh->ps.bw_period - 1);
+       qh->ps.phase_uf = uframe;
+       qh->ps.cs_mask = qh->ps.period ?
+                       (c_mask << 8) | (1 << uframe) :
+                       QH_SMASK;
+
+       /* reset S-frame and (maybe) C-frame masks */
+       hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
+       hw->hw_info2 |= cpu_to_hc32(ehci, qh->ps.cs_mask);
+       reserve_release_intr_bandwidth(ehci, qh, 1);
 
 done:
        return status;
@@ -969,7 +1030,8 @@ iso_stream_alloc (gfp_t mem_flags)
        if (likely (stream != NULL)) {
                INIT_LIST_HEAD(&stream->td_list);
                INIT_LIST_HEAD(&stream->free_list);
-               stream->next_uframe = -1;
+               stream->next_uframe = NO_FRAME;
+               stream->ps.phase = NO_FRAME;
        }
        return stream;
 }
@@ -978,25 +1040,24 @@ static void
 iso_stream_init (
        struct ehci_hcd         *ehci,
        struct ehci_iso_stream  *stream,
-       struct usb_device       *dev,
-       int                     pipe,
-       unsigned                interval
+       struct urb              *urb
 )
 {
        static const u8 smask_out [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f };
 
+       struct usb_device       *dev = urb->dev;
        u32                     buf1;
        unsigned                epnum, maxp;
        int                     is_input;
-       long                    bandwidth;
+       unsigned                tmp;
 
        /*
         * this might be a "high bandwidth" highspeed endpoint,
         * as encoded in the ep descriptor's wMaxPacket field
         */
-       epnum = usb_pipeendpoint (pipe);
-       is_input = usb_pipein (pipe) ? USB_DIR_IN : 0;
-       maxp = usb_maxpacket(dev, pipe, !is_input);
+       epnum = usb_pipeendpoint(urb->pipe);
+       is_input = usb_pipein(urb->pipe) ? USB_DIR_IN : 0;
+       maxp = usb_endpoint_maxp(&urb->ep->desc);
        if (is_input) {
                buf1 = (1 << 11);
        } else {
@@ -1020,9 +1081,19 @@ iso_stream_init (
                /* usbfs wants to report the average usecs per frame tied up
                 * when transfers on this endpoint are scheduled ...
                 */
-               stream->usecs = HS_USECS_ISO (maxp);
-               bandwidth = stream->usecs * 8;
-               bandwidth /= interval;
+               stream->ps.usecs = HS_USECS_ISO(maxp);
+
+               /* period for bandwidth allocation */
+               tmp = min_t(unsigned, EHCI_BANDWIDTH_SIZE,
+                               1 << (urb->ep->desc.bInterval - 1));
+
+               /* Allow urb->interval to override */
+               stream->ps.bw_uperiod = min_t(unsigned, tmp, urb->interval);
+
+               stream->uperiod = urb->interval;
+               stream->ps.period = urb->interval >> 3;
+               stream->bandwidth = stream->ps.usecs * 8 /
+                               stream->ps.bw_uperiod;
 
        } else {
                u32             addr;
@@ -1036,36 +1107,46 @@ iso_stream_init (
                        addr |= dev->tt->hub->devnum << 16;
                addr |= epnum << 8;
                addr |= dev->devnum;
-               stream->usecs = HS_USECS_ISO (maxp);
+               stream->ps.usecs = HS_USECS_ISO(maxp);
                think_time = dev->tt ? dev->tt->think_time : 0;
-               stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time (
+               stream->ps.tt_usecs = NS_TO_US(think_time + usb_calc_bus_time(
                                dev->speed, is_input, 1, maxp));
                hs_transfers = max (1u, (maxp + 187) / 188);
                if (is_input) {
                        u32     tmp;
 
                        addr |= 1 << 31;
-                       stream->c_usecs = stream->usecs;
-                       stream->usecs = HS_USECS_ISO (1);
-                       stream->raw_mask = 1;
+                       stream->ps.c_usecs = stream->ps.usecs;
+                       stream->ps.usecs = HS_USECS_ISO(1);
+                       stream->ps.cs_mask = 1;
 
                        /* c-mask as specified in USB 2.0 11.18.4 3.c */
                        tmp = (1 << (hs_transfers + 2)) - 1;
-                       stream->raw_mask |= tmp << (8 + 2);
+                       stream->ps.cs_mask |= tmp << (8 + 2);
                } else
-                       stream->raw_mask = smask_out [hs_transfers - 1];
-               bandwidth = stream->usecs + stream->c_usecs;
-               bandwidth /= interval << 3;
+                       stream->ps.cs_mask = smask_out[hs_transfers - 1];
+
+               /* period for bandwidth allocation */
+               tmp = min_t(unsigned, EHCI_BANDWIDTH_FRAMES,
+                               1 << (urb->ep->desc.bInterval - 1));
+
+               /* Allow urb->interval to override */
+               stream->ps.bw_period = min_t(unsigned, tmp, urb->interval);
+               stream->ps.bw_uperiod = stream->ps.bw_period << 3;
 
-               /* stream->splits gets created from raw_mask later */
+               stream->ps.period = urb->interval;
+               stream->uperiod = urb->interval << 3;
+               stream->bandwidth = (stream->ps.usecs + stream->ps.c_usecs) /
+                               stream->ps.bw_period;
+
+               /* stream->splits gets created from cs_mask later */
                stream->address = cpu_to_hc32(ehci, addr);
        }
-       stream->bandwidth = bandwidth;
 
-       stream->udev = dev;
+       stream->ps.udev = dev;
+       stream->ps.ep = urb->ep;
 
        stream->bEndpointAddress = is_input | epnum;
-       stream->interval = interval;
        stream->maxp = maxp;
 }
 
@@ -1090,9 +1171,7 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
                stream = iso_stream_alloc(GFP_ATOMIC);
                if (likely (stream != NULL)) {
                        ep->hcpriv = stream;
-                       stream->ep = ep;
-                       iso_stream_init(ehci, stream, urb->dev, urb->pipe,
-                                       urb->interval);
+                       iso_stream_init(ehci, stream, urb);
                }
 
        /* if dev->ep [epnum] is a QH, hw is set */
@@ -1137,7 +1216,7 @@ itd_sched_init(
        dma_addr_t      dma = urb->transfer_dma;
 
        /* how many uframes are needed for these transfers */
-       iso_sched->span = urb->number_of_packets * stream->interval;
+       iso_sched->span = urb->number_of_packets * stream->uperiod;
 
        /* figure out per-uframe itd fields that we'll need later
         * when we fit new itds into the schedule.
@@ -1236,7 +1315,7 @@ itd_urb_transaction (
 
                memset (itd, 0, sizeof *itd);
                itd->itd_dma = itd_dma;
-               itd->frame = 9999;              /* an invalid value */
+               itd->frame = NO_FRAME;
                list_add (&itd->itd_list, &sched->td_list);
        }
        spin_unlock_irqrestore (&ehci->lock, flags);
@@ -1249,49 +1328,106 @@ itd_urb_transaction (
 
 /*-------------------------------------------------------------------------*/
 
+static void reserve_release_iso_bandwidth(struct ehci_hcd *ehci,
+               struct ehci_iso_stream *stream, int sign)
+{
+       unsigned                uframe;
+       unsigned                i, j;
+       unsigned                s_mask, c_mask, m;
+       int                     usecs = stream->ps.usecs;
+       int                     c_usecs = stream->ps.c_usecs;
+       int                     tt_usecs = stream->ps.tt_usecs;
+       struct ehci_tt          *tt;
+
+       if (stream->ps.phase == NO_FRAME)       /* Bandwidth wasn't reserved */
+               return;
+       uframe = stream->ps.bw_phase << 3;
+
+       bandwidth_dbg(ehci, sign, "iso", &stream->ps);
+
+       if (sign < 0) {         /* Release bandwidth */
+               usecs = -usecs;
+               c_usecs = -c_usecs;
+               tt_usecs = -tt_usecs;
+       }
+
+       if (!stream->splits) {          /* High speed */
+               for (i = uframe + stream->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE;
+                               i += stream->ps.bw_uperiod)
+                       ehci->bandwidth[i] += usecs;
+
+       } else {                        /* Full speed */
+               s_mask = stream->ps.cs_mask;
+               c_mask = s_mask >> 8;
+
+               /* NOTE: adjustment needed for frame overflow */
+               for (i = uframe; i < EHCI_BANDWIDTH_SIZE;
+                               i += stream->ps.bw_uperiod) {
+                       for ((j = stream->ps.phase_uf, m = 1 << j); j < 8;
+                                       (++j, m <<= 1)) {
+                               if (s_mask & m)
+                                       ehci->bandwidth[i+j] += usecs;
+                               else if (c_mask & m)
+                                       ehci->bandwidth[i+j] += c_usecs;
+                       }
+               }
+
+               tt = find_tt(stream->ps.udev);
+               if (sign > 0)
+                       list_add_tail(&stream->ps.ps_list, &tt->ps_list);
+               else
+                       list_del(&stream->ps.ps_list);
+
+               for (i = uframe >> 3; i < EHCI_BANDWIDTH_FRAMES;
+                               i += stream->ps.bw_period)
+                       tt->bandwidth[i] += tt_usecs;
+       }
+}
+
 static inline int
 itd_slot_ok (
        struct ehci_hcd         *ehci,
-       u32                     mod,
-       u32                     uframe,
-       u8                      usecs,
-       u32                     period
+       struct ehci_iso_stream  *stream,
+       unsigned                uframe
 )
 {
-       uframe %= period;
-       do {
-               /* can't commit more than uframe_periodic_max usec */
-               if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
-                               > (ehci->uframe_periodic_max - usecs))
-                       return 0;
+       unsigned                usecs;
+
+       /* convert "usecs we need" to "max already claimed" */
+       usecs = ehci->uframe_periodic_max - stream->ps.usecs;
 
-               /* we know urb->interval is 2^N uframes */
-               uframe += period;
-       } while (uframe < mod);
+       for (uframe &= stream->ps.bw_uperiod - 1; uframe < EHCI_BANDWIDTH_SIZE;
+                       uframe += stream->ps.bw_uperiod) {
+               if (ehci->bandwidth[uframe] > usecs)
+                       return 0;
+       }
        return 1;
 }
 
 static inline int
 sitd_slot_ok (
        struct ehci_hcd         *ehci,
-       u32                     mod,
        struct ehci_iso_stream  *stream,
-       u32                     uframe,
+       unsigned                uframe,
        struct ehci_iso_sched   *sched,
-       u32                     period_uframes
+       struct ehci_tt          *tt
 )
 {
-       u32                     mask, tmp;
-       u32                     frame, uf;
+       unsigned                mask, tmp;
+       unsigned                frame, uf;
+
+       mask = stream->ps.cs_mask << (uframe & 7);
 
-       mask = stream->raw_mask << (uframe & 7);
+       /* for OUT, don't wrap SSPLIT into H-microframe 7 */
+       if (((stream->ps.cs_mask & 0xff) << (uframe & 7)) >= (1 << 7))
+               return 0;
 
        /* for IN, don't wrap CSPLIT into the next frame */
        if (mask & ~0xffff)
                return 0;
 
        /* check bandwidth */
-       uframe %= period_uframes;
+       uframe &= stream->ps.bw_uperiod - 1;
        frame = uframe >> 3;
 
 #ifdef CONFIG_USB_EHCI_TT_NEWSCHED
@@ -1299,54 +1435,48 @@ sitd_slot_ok (
         * tt_available scheduling guarantees 10+% for control/bulk.
         */
        uf = uframe & 7;
-       if (!tt_available(ehci, period_uframes >> 3,
-                       stream->udev, frame, uf, stream->tt_usecs))
+       if (!tt_available(ehci, &stream->ps, tt, frame, uf))
                return 0;
 #else
        /* tt must be idle for start(s), any gap, and csplit.
         * assume scheduling slop leaves 10+% for control/bulk.
         */
-       if (!tt_no_collision(ehci, period_uframes >> 3,
-                       stream->udev, frame, mask))
+       if (!tt_no_collision(ehci, stream->ps.bw_period,
+                       stream->ps.udev, frame, mask))
                return 0;
 #endif
 
-       /* this multi-pass logic is simple, but performance may
-        * suffer when the schedule data isn't cached.
-        */
        do {
-               u32             max_used;
-
-               frame = uframe >> 3;
-               uf = uframe & 7;
+               unsigned        max_used;
+               unsigned        i;
 
                /* check starts (OUT uses more than one) */
-               max_used = ehci->uframe_periodic_max - stream->usecs;
-               for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) {
-                       if (periodic_usecs (ehci, frame, uf) > max_used)
+               uf = uframe;
+               max_used = ehci->uframe_periodic_max - stream->ps.usecs;
+               for (tmp = stream->ps.cs_mask & 0xff; tmp; tmp >>= 1, uf++) {
+                       if (ehci->bandwidth[uf] > max_used)
                                return 0;
                }
 
                /* for IN, check CSPLIT */
-               if (stream->c_usecs) {
-                       uf = uframe & 7;
-                       max_used = ehci->uframe_periodic_max - stream->c_usecs;
-                       do {
-                               tmp = 1 << uf;
-                               tmp <<= 8;
-                               if ((stream->raw_mask & tmp) == 0)
+               if (stream->ps.c_usecs) {
+                       max_used = ehci->uframe_periodic_max -
+                                       stream->ps.c_usecs;
+                       uf = uframe & ~7;
+                       tmp = 1 << (2+8);
+                       for (i = (uframe & 7) + 2; i < 8; (++i, tmp <<= 1)) {
+                               if ((stream->ps.cs_mask & tmp) == 0)
                                        continue;
-                               if (periodic_usecs (ehci, frame, uf)
-                                               > max_used)
+                               if (ehci->bandwidth[uf+i] > max_used)
                                        return 0;
-                       } while (++uf < 8);
+                       }
                }
 
-               /* we know urb->interval is 2^N uframes */
-               uframe += period_uframes;
-       } while (uframe < mod);
+               uframe += stream->ps.bw_uperiod;
+       } while (uframe < EHCI_BANDWIDTH_SIZE);
 
-       stream->splits = cpu_to_hc32(ehci, stream->raw_mask << (uframe & 7));
+       stream->ps.cs_mask <<= uframe & 7;
+       stream->splits = cpu_to_hc32(ehci, stream->ps.cs_mask);
        return 1;
 }
 
@@ -1361,8 +1491,6 @@ sitd_slot_ok (
  * given EHCI_TUNE_FLS and the slop).  Or, write a smarter scheduler!
  */
 
-#define SCHEDULING_DELAY       40      /* microframes */
-
 static int
 iso_stream_schedule (
        struct ehci_hcd         *ehci,
@@ -1370,134 +1498,184 @@ iso_stream_schedule (
        struct ehci_iso_stream  *stream
 )
 {
-       u32                     now, base, next, start, period, span;
-       int                     status;
+       u32                     now, base, next, start, period, span, now2;
+       u32                     wrap = 0, skip = 0;
+       int                     status = 0;
        unsigned                mod = ehci->periodic_size << 3;
        struct ehci_iso_sched   *sched = urb->hcpriv;
+       bool                    empty = list_empty(&stream->td_list);
+       bool                    new_stream = false;
 
-       period = urb->interval;
+       period = stream->uperiod;
        span = sched->span;
-       if (!stream->highspeed) {
-               period <<= 3;
+       if (!stream->highspeed)
                span <<= 3;
-       }
 
-       now = ehci_read_frame_index(ehci) & (mod - 1);
+       /* Start a new isochronous stream? */
+       if (unlikely(empty && !hcd_periodic_completion_in_progress(
+                       ehci_to_hcd(ehci), urb->ep))) {
 
-       /* Typical case: reuse current schedule, stream is still active.
-        * Hopefully there are no gaps from the host falling behind
-        * (irq delays etc).  If there are, the behavior depends on
-        * whether URB_ISO_ASAP is set.
-        */
-       if (likely (!list_empty (&stream->td_list))) {
+               /* Schedule the endpoint */
+               if (stream->ps.phase == NO_FRAME) {
+                       int             done = 0;
+                       struct ehci_tt  *tt = find_tt(stream->ps.udev);
 
-               /* Take the isochronous scheduling threshold into account */
-               if (ehci->i_thresh)
-                       next = now + ehci->i_thresh;    /* uframe cache */
-               else
-                       next = (now + 2 + 7) & ~0x07;   /* full frame cache */
-
-               /*
-                * Use ehci->last_iso_frame as the base.  There can't be any
-                * TDs scheduled for earlier than that.
-                */
-               base = ehci->last_iso_frame << 3;
-               next = (next - base) & (mod - 1);
-               start = (stream->next_uframe - base) & (mod - 1);
-
-               /* Is the schedule already full? */
-               if (unlikely(start < period)) {
-                       ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n",
-                                       urb, stream->next_uframe, base,
-                                       period, mod);
-                       status = -ENOSPC;
-                       goto fail;
-               }
-
-               /* Behind the scheduling threshold? */
-               if (unlikely(start < next)) {
-                       unsigned now2 = (now - base) & (mod - 1);
+                       if (IS_ERR(tt)) {
+                               status = PTR_ERR(tt);
+                               goto fail;
+                       }
+                       compute_tt_budget(ehci->tt_budget, tt);
 
-                       /* USB_ISO_ASAP: Round up to the first available slot */
-                       if (urb->transfer_flags & URB_ISO_ASAP)
-                               start += (next - start + period - 1) & -period;
+                       start = ((-(++ehci->random_frame)) << 3) & (period - 1);
 
-                       /*
-                        * Not ASAP: Use the next slot in the stream,
-                        * no matter what.
+                       /* find a uframe slot with enough bandwidth.
+                        * Early uframes are more precious because full-speed
+                        * iso IN transfers can't use late uframes,
+                        * and therefore they should be allocated last.
                         */
-                       else if (start + span - period < now2) {
-                               ehci_dbg(ehci, "iso underrun %p (%u+%u < %u)\n",
-                                               urb, start + base,
-                                               span - period, now2 + base);
+                       next = start;
+                       start += period;
+                       do {
+                               start--;
+                               /* check schedule: enough space? */
+                               if (stream->highspeed) {
+                                       if (itd_slot_ok(ehci, stream, start))
+                                               done = 1;
+                               } else {
+                                       if ((start % 8) >= 6)
+                                               continue;
+                                       if (sitd_slot_ok(ehci, stream, start,
+                                                       sched, tt))
+                                               done = 1;
+                               }
+                       } while (start > next && !done);
+
+                       /* no room in the schedule */
+                       if (!done) {
+                               ehci_dbg(ehci, "iso sched full %p", urb);
+                               status = -ENOSPC;
+                               goto fail;
                        }
+                       stream->ps.phase = (start >> 3) &
+                                       (stream->ps.period - 1);
+                       stream->ps.bw_phase = stream->ps.phase &
+                                       (stream->ps.bw_period - 1);
+                       stream->ps.phase_uf = start & 7;
+                       reserve_release_iso_bandwidth(ehci, stream, 1);
+               }
+
+               /* New stream is already scheduled; use the upcoming slot */
+               else {
+                       start = (stream->ps.phase << 3) + stream->ps.phase_uf;
                }
 
-               start += base;
+               stream->next_uframe = start;
+               new_stream = true;
        }
 
-       /* need to schedule; when's the next (u)frame we could start?
-        * this is bigger than ehci->i_thresh allows; scheduling itself
-        * isn't free, the delay should handle reasonably slow cpus.  it
-        * can also help high bandwidth if the dma and irq loads don't
-        * jump until after the queue is primed.
+       now = ehci_read_frame_index(ehci) & (mod - 1);
+
+       /* Take the isochronous scheduling threshold into account */
+       if (ehci->i_thresh)
+               next = now + ehci->i_thresh;    /* uframe cache */
+       else
+               next = (now + 2 + 7) & ~0x07;   /* full frame cache */
+
+       /*
+        * Use ehci->last_iso_frame as the base.  There can't be any
+        * TDs scheduled for earlier than that.
         */
-       else {
-               int done = 0;
+       base = ehci->last_iso_frame << 3;
+       next = (next - base) & (mod - 1);
+       start = (stream->next_uframe - base) & (mod - 1);
 
-               base = now & ~0x07;
-               start = base + SCHEDULING_DELAY;
+       if (unlikely(new_stream))
+               goto do_ASAP;
 
-               /* find a uframe slot with enough bandwidth.
-                * Early uframes are more precious because full-speed
-                * iso IN transfers can't use late uframes,
-                * and therefore they should be allocated last.
-                */
-               next = start;
-               start += period;
-               do {
-                       start--;
-                       /* check schedule: enough space? */
-                       if (stream->highspeed) {
-                               if (itd_slot_ok(ehci, mod, start,
-                                               stream->usecs, period))
-                                       done = 1;
-                       } else {
-                               if ((start % 8) >= 6)
-                                       continue;
-                               if (sitd_slot_ok(ehci, mod, stream,
-                                               start, sched, period))
-                                       done = 1;
-                       }
-               } while (start > next && !done);
+       /*
+        * Typical case: reuse current schedule, stream may still be active.
+        * Hopefully there are no gaps from the host falling behind
+        * (irq delays etc).  If there are, the behavior depends on
+        * whether URB_ISO_ASAP is set.
+        */
+       now2 = (now - base) & (mod - 1);
+
+       /* Is the schedule already full? */
+       if (unlikely(!empty && start < period)) {
+               ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n",
+                               urb, stream->next_uframe, base, period, mod);
+               status = -ENOSPC;
+               goto fail;
+       }
+
+       /* Is the next packet scheduled after the base time? */
+       if (likely(!empty || start <= now2 + period)) {
+
+               /* URB_ISO_ASAP: make sure that start >= next */
+               if (unlikely(start < next &&
+                               (urb->transfer_flags & URB_ISO_ASAP)))
+                       goto do_ASAP;
+
+               /* Otherwise use start, if it's not in the past */
+               if (likely(start >= now2))
+                       goto use_start;
 
-               /* no room in the schedule */
-               if (!done) {
-                       ehci_dbg(ehci, "iso sched full %p", urb);
-                       status = -ENOSPC;
-                       goto fail;
+       /* Otherwise we got an underrun while the queue was empty */
+       } else {
+               if (urb->transfer_flags & URB_ISO_ASAP)
+                       goto do_ASAP;
+               wrap = mod;
+               now2 += mod;
+       }
+
+       /* How many uframes and packets do we need to skip? */
+       skip = (now2 - start + period - 1) & -period;
+       if (skip >= span) {             /* Entirely in the past? */
+               ehci_dbg(ehci, "iso underrun %p (%u+%u < %u) [%u]\n",
+                               urb, start + base, span - period, now2 + base,
+                               base);
+
+               /* Try to keep the last TD intact for scanning later */
+               skip = span - period;
+
+               /* Will it come before the current scan position? */
+               if (empty) {
+                       skip = span;    /* Skip the entire URB */
+                       status = 1;     /* and give it back immediately */
+                       iso_sched_free(stream, sched);
+                       sched = NULL;
                }
        }
+       urb->error_count = skip / period;
+       if (sched)
+               sched->first_packet = urb->error_count;
+       goto use_start;
 
+ do_ASAP:
+       /* Use the first slot after "next" */
+       start = next + ((start - next) & (period - 1));
+
+ use_start:
        /* Tried to schedule too far into the future? */
-       if (unlikely(start - base + span - period >= mod)) {
+       if (unlikely(start + span - period >= mod + wrap)) {
                ehci_dbg(ehci, "request %p would overflow (%u+%u >= %u)\n",
-                               urb, start - base, span - period, mod);
+                               urb, start, span - period, mod + wrap);
                status = -EFBIG;
                goto fail;
        }
 
-       stream->next_uframe = start & (mod - 1);
+       start += base;
+       stream->next_uframe = (start + skip) & (mod - 1);
 
        /* report high speed start in uframes; full speed, in frames */
-       urb->start_frame = stream->next_uframe;
+       urb->start_frame = start & (mod - 1);
        if (!stream->highspeed)
                urb->start_frame >>= 3;
 
        /* Make sure scan_isoc() sees these */
        if (ehci->isoc_count == 0)
                ehci->last_iso_frame = now >> 3;
-       return 0;
+       return status;
 
  fail:
        iso_sched_free(stream, sched);
@@ -1610,7 +1788,8 @@ static void itd_link_urb(
        ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
 
        /* fill iTDs uframe by uframe */
-       for (packet = 0, itd = NULL; packet < urb->number_of_packets; ) {
+       for (packet = iso_sched->first_packet, itd = NULL;
+                       packet < urb->number_of_packets;) {
                if (itd == NULL) {
                        /* ASSERT:  we have all necessary itds */
                        // BUG_ON (list_empty (&iso_sched->td_list));
@@ -1630,7 +1809,7 @@ static void itd_link_urb(
 
                itd_patch(ehci, itd, iso_sched, packet, uframe);
 
-               next_uframe += stream->interval;
+               next_uframe += stream->uperiod;
                next_uframe &= mod - 1;
                packet++;
 
@@ -1770,9 +1949,9 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
                ehci_dbg (ehci, "can't get iso stream\n");
                return -ENOMEM;
        }
-       if (unlikely (urb->interval != stream->interval)) {
+       if (unlikely(urb->interval != stream->uperiod)) {
                ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
-                       stream->interval, urb->interval);
+                       stream->uperiod, urb->interval);
                goto done;
        }
 
@@ -1804,10 +1983,14 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
        if (unlikely(status))
                goto done_not_linked;
        status = iso_stream_schedule(ehci, urb, stream);
-       if (likely (status == 0))
+       if (likely(status == 0)) {
                itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
-       else
+       } else if (status > 0) {
+               status = 0;
+               ehci_urb_done(ehci, urb, 0);
+       } else {
                usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+       }
  done_not_linked:
        spin_unlock_irqrestore (&ehci->lock, flags);
  done:
@@ -1833,7 +2016,7 @@ sitd_sched_init(
        dma_addr_t      dma = urb->transfer_dma;
 
        /* how many frames are needed for these transfers */
-       iso_sched->span = urb->number_of_packets * stream->interval;
+       iso_sched->span = urb->number_of_packets * stream->ps.period;
 
        /* figure out per-frame sitd fields that we'll need later
         * when we fit new sitds into the schedule.
@@ -1925,7 +2108,7 @@ sitd_urb_transaction (
 
                memset (sitd, 0, sizeof *sitd);
                sitd->sitd_dma = sitd_dma;
-               sitd->frame = 9999;             /* an invalid value */
+               sitd->frame = NO_FRAME;
                list_add (&sitd->sitd_list, &iso_sched->td_list);
        }
 
@@ -2008,7 +2191,7 @@ static void sitd_link_urb(
        ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
 
        /* fill sITDs frame by frame */
-       for (packet = 0, sitd = NULL;
+       for (packet = sched->first_packet, sitd = NULL;
                        packet < urb->number_of_packets;
                        packet++) {
 
@@ -2027,7 +2210,7 @@ static void sitd_link_urb(
                sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1),
                                sitd);
 
-               next_uframe += stream->interval << 3;
+               next_uframe += stream->uperiod;
        }
        stream->next_uframe = next_uframe & (mod - 1);
 
@@ -2146,9 +2329,9 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
                ehci_dbg (ehci, "can't get iso stream\n");
                return -ENOMEM;
        }
-       if (urb->interval != stream->interval) {
+       if (urb->interval != stream->ps.period) {
                ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
-                       stream->interval, urb->interval);
+                       stream->ps.period, urb->interval);
                goto done;
        }
 
@@ -2178,10 +2361,14 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
        if (unlikely(status))
                goto done_not_linked;
        status = iso_stream_schedule(ehci, urb, stream);
-       if (status == 0)
+       if (likely(status == 0)) {
                sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
-       else
+       } else if (status > 0) {
+               status = 0;
+               ehci_urb_done(ehci, urb, 0);
+       } else {
                usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+       }
  done_not_linked:
        spin_unlock_irqrestore (&ehci->lock, flags);
  done:
@@ -2259,7 +2446,8 @@ restart:
                                    q.itd->hw_next != EHCI_LIST_END(ehci))
                                        *hw_p = q.itd->hw_next;
                                else
-                                       *hw_p = ehci->dummy->qh_dma;
+                                       *hw_p = cpu_to_hc32(ehci,
+                                                       ehci->dummy->qh_dma);
                                type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
                                wmb();
                                modified = itd_complete (ehci, q.itd);
@@ -2294,7 +2482,8 @@ restart:
                                    q.sitd->hw_next != EHCI_LIST_END(ehci))
                                        *hw_p = q.sitd->hw_next;
                                else
-                                       *hw_p = ehci->dummy->qh_dma;
+                                       *hw_p = cpu_to_hc32(ehci,
+                                                       ehci->dummy->qh_dma);
                                type = Q_NEXT_TYPE(ehci, q.sitd->hw_next);
                                wmb();
                                modified = sitd_complete (ehci, q.sitd);
index b2de52d3961488f249aeb9d4026efe2d603b72bb..8a734498079bc176938c57a6b132c146e9be00dd 100644 (file)
@@ -55,7 +55,7 @@ const struct hc_driver ehci_sead3_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 93e59a13bc1fec919ef81690bbce9f4bdcd3e951..dc899eb2b86183561351d78e8dba1ceffc9cbb18 100644 (file)
@@ -36,7 +36,7 @@ static const struct hc_driver ehci_sh_hc_driver = {
         * generic hardware linkage
         */
        .irq                            = ehci_irq,
-       .flags                          = HCD_USB2 | HCD_MEMORY,
+       .flags                          = HCD_USB2 | HCD_MEMORY | HCD_BH,
 
        /*
         * basic lifecycle operations
index 14ced00ba220abded902fe7832d598fa37eeffb3..f6459dfb6f54fd1a08c2780dd804a906ccca539b 100644 (file)
@@ -97,8 +97,7 @@ static ssize_t store_uframe_periodic_max(struct device *dev,
 {
        struct ehci_hcd         *ehci;
        unsigned                uframe_periodic_max;
-       unsigned                frame, uframe;
-       unsigned short          allocated_max;
+       unsigned                uframe;
        unsigned long           flags;
        ssize_t                 ret;
 
@@ -122,16 +121,14 @@ static ssize_t store_uframe_periodic_max(struct device *dev,
 
        /*
         * for request to decrease max periodic bandwidth, we have to check
-        * every microframe in the schedule to see whether the decrease is
-        * possible.
+        * to see whether the decrease is possible.
         */
        if (uframe_periodic_max < ehci->uframe_periodic_max) {
-               allocated_max = 0;
+               u8              allocated_max = 0;
 
-               for (frame = 0; frame < ehci->periodic_size; ++frame)
-                       for (uframe = 0; uframe < 7; ++uframe)
-                               allocated_max = max(allocated_max,
-                                                   periodic_usecs (ehci, frame, uframe));
+               for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe)
+                       allocated_max = max(allocated_max,
+                                       ehci->bandwidth[uframe]);
 
                if (allocated_max > uframe_periodic_max) {
                        ehci_info(ehci,
index 78fa76da332435a83cc3c55226d33cbeb2e78afe..e6d8e26e48cc935f1d9c71bd29aa344b4e54dd03 100644 (file)
@@ -388,7 +388,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
 
        err = clk_prepare_enable(tegra->clk);
        if (err)
-               goto cleanup_clk_get;
+               goto cleanup_hcd_create;
 
        tegra_periph_reset_assert(tegra->clk);
        udelay(1);
@@ -465,8 +465,6 @@ cleanup_phy:
        usb_phy_shutdown(hcd->phy);
 cleanup_clk_en:
        clk_disable_unprepare(tegra->clk);
-cleanup_clk_get:
-       clk_put(tegra->clk);
 cleanup_hcd_create:
        usb_put_hcd(hcd);
        return err;
index cca4be90a864dba009c852f606653fb5fe60d568..67026ffbf9a871c9780a4b0b7f0d6c739c904e73 100644 (file)
@@ -61,7 +61,7 @@ static const struct hc_driver ehci_tilegx_hc_driver = {
         * Generic hardware linkage.
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * Basic lifecycle operations.
index 59e0e24c753febfb76369be8f872cb3731c2f365..cdad8438c02b3171567f5e0084ba46ee480db669 100644 (file)
  *
  */
 
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
 
 /* enable phy0 and phy1 for w90p910 */
 #define        ENPHY           (0x01<<8)
 #define PHY0_CTR       (0xA4)
 #define PHY1_CTR       (0xA8)
 
+#define DRIVER_DESC "EHCI w90x900 driver"
+
+static const char hcd_name[] = "ehci-w90x900 ";
+
+static struct hc_driver __read_mostly ehci_w90x900_hc_driver;
+
 static int usb_w90x900_probe(const struct hc_driver *driver,
                      struct platform_device *pdev)
 {
@@ -90,8 +105,8 @@ err1:
        return retval;
 }
 
-static
-void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev)
+static void usb_w90x900_remove(struct usb_hcd *hcd,
+                       struct platform_device *pdev)
 {
        usb_remove_hcd(hcd);
        iounmap(hcd->regs);
@@ -99,54 +114,6 @@ void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev)
        usb_put_hcd(hcd);
 }
 
-static const struct hc_driver ehci_w90x900_hc_driver = {
-       .description = hcd_name,
-       .product_desc = "Nuvoton w90x900 EHCI Host Controller",
-       .hcd_priv_size = sizeof(struct ehci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq = ehci_irq,
-       .flags = HCD_USB2|HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset = ehci_setup,
-       .start = ehci_run,
-
-       .stop = ehci_stop,
-       .shutdown = ehci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue = ehci_urb_enqueue,
-       .urb_dequeue = ehci_urb_dequeue,
-       .endpoint_disable = ehci_endpoint_disable,
-       .endpoint_reset = ehci_endpoint_reset,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number = ehci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data = ehci_hub_status_data,
-       .hub_control = ehci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend = ehci_bus_suspend,
-       .bus_resume = ehci_bus_resume,
-#endif
-       .relinquish_port        = ehci_relinquish_port,
-       .port_handed_over       = ehci_port_handed_over,
-
-       .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
-};
-
 static int ehci_w90x900_probe(struct platform_device *pdev)
 {
        if (usb_disabled())
@@ -173,7 +140,25 @@ static struct platform_driver ehci_hcd_w90x900_driver = {
        },
 };
 
+static int __init ehci_w90X900_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ehci_init_driver(&ehci_w90x900_hc_driver, NULL);
+       return platform_driver_register(&ehci_hcd_w90x900_driver);
+}
+module_init(ehci_w90X900_init);
+
+static void __exit ehci_w90X900_cleanup(void)
+{
+       platform_driver_unregister(&ehci_hcd_w90x900_driver);
+}
+module_exit(ehci_w90X900_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
-MODULE_DESCRIPTION("w90p910 usb ehci driver!");
-MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:w90p910-ehci");
+MODULE_LICENSE("GPL v2");
index eba962e6ebfbbd8ffdd46a85e0fed5ec9b18b78a..95979f9f4381d8e8e573c7e0fe254d5585d23ab8 100644 (file)
@@ -79,7 +79,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 291db7d09f220fe24dfa8bf9c21f1528a0d46a59..e8f41c5e771b978ddcd42b49cd1b638fdcc89a57 100644 (file)
@@ -54,6 +54,28 @@ struct ehci_stats {
        unsigned long           unlink;
 };
 
+/*
+ * Scheduling and budgeting information for periodic transfers, for both
+ * high-speed devices and full/low-speed devices lying behind a TT.
+ */
+struct ehci_per_sched {
+       struct usb_device       *udev;          /* access to the TT */
+       struct usb_host_endpoint *ep;
+       struct list_head        ps_list;        /* node on ehci_tt's ps_list */
+       u16                     tt_usecs;       /* time on the FS/LS bus */
+       u16                     cs_mask;        /* C-mask and S-mask bytes */
+       u16                     period;         /* actual period in frames */
+       u16                     phase;          /* actual phase, frame part */
+       u8                      bw_phase;       /* same, for bandwidth
+                                                  reservation */
+       u8                      phase_uf;       /* uframe part of the phase */
+       u8                      usecs, c_usecs; /* times on the HS bus */
+       u8                      bw_uperiod;     /* period in microframes, for
+                                                  bandwidth reservation */
+       u8                      bw_period;      /* same, in frames */
+};
+#define NO_FRAME       29999                   /* frame not assigned yet */
+
 /* ehci_hcd->lock guards shared data against other CPUs:
  *   ehci_hcd: async, unlink, periodic (and shadow), ...
  *   usb_host_endpoint: hcpriv
@@ -230,6 +252,15 @@ struct ehci_hcd {                  /* one per controller */
        struct dentry           *debug_dir;
 #endif
 
+       /* bandwidth usage */
+#define EHCI_BANDWIDTH_SIZE    64
+#define EHCI_BANDWIDTH_FRAMES  (EHCI_BANDWIDTH_SIZE >> 3)
+       u8                      bandwidth[EHCI_BANDWIDTH_SIZE];
+                                               /* us allocated per uframe */
+       u8                      tt_budget[EHCI_BANDWIDTH_SIZE];
+                                               /* us budgeted per uframe */
+       struct list_head        tt_list;
+
        /* platform-specific data -- must come last */
        unsigned long           priv[0] __aligned(sizeof(s64));
 };
@@ -385,6 +416,7 @@ struct ehci_qh {
        struct list_head        intr_node;      /* list of intr QHs */
        struct ehci_qtd         *dummy;
        struct list_head        unlink_node;
+       struct ehci_per_sched   ps;             /* scheduling info */
 
        unsigned                unlink_cycle;
 
@@ -398,16 +430,8 @@ struct ehci_qh {
        u8                      xacterrs;       /* XactErr retry counter */
 #define        QH_XACTERR_MAX          32              /* XactErr retry limit */
 
-       /* periodic schedule info */
-       u8                      usecs;          /* intr bandwidth */
        u8                      gap_uf;         /* uframes split/csplit gap */
-       u8                      c_usecs;        /* ... split completion bw */
-       u16                     tt_usecs;       /* tt downstream bandwidth */
-       unsigned short          period;         /* polling interval */
-       unsigned short          start;          /* where polling starts */
-#define NO_FRAME ((unsigned short)~0)                  /* pick new start */
 
-       struct usb_device       *dev;           /* access to TT */
        unsigned                is_out:1;       /* bulk or intr OUT */
        unsigned                clearing_tt:1;  /* Clear-TT-Buf in progress */
        unsigned                dequeue_during_giveback:1;
@@ -434,6 +458,7 @@ struct ehci_iso_packet {
 struct ehci_iso_sched {
        struct list_head        td_list;
        unsigned                span;
+       unsigned                first_packet;
        struct ehci_iso_packet  packet [0];
 };
 
@@ -449,22 +474,17 @@ struct ehci_iso_stream {
        u8                      highspeed;
        struct list_head        td_list;        /* queued itds/sitds */
        struct list_head        free_list;      /* list of unused itds/sitds */
-       struct usb_device       *udev;
-       struct usb_host_endpoint *ep;
 
        /* output of (re)scheduling */
-       int                     next_uframe;
+       struct ehci_per_sched   ps;             /* scheduling info */
+       unsigned                next_uframe;
        __hc32                  splits;
 
        /* the rest is derived from the endpoint descriptor,
-        * trusting urb->interval == f(epdesc->bInterval) and
         * including the extra info for hw_bufp[0..2]
         */
-       u8                      usecs, c_usecs;
-       u16                     interval;
-       u16                     tt_usecs;
+       u16                     uperiod;        /* period in uframes */
        u16                     maxp;
-       u16                     raw_mask;
        unsigned                bandwidth;
 
        /* This is used to initialize iTD's hw_bufp fields */
@@ -579,6 +599,35 @@ struct ehci_fstn {
 
 /*-------------------------------------------------------------------------*/
 
+/*
+ * USB-2.0 Specification Sections 11.14 and 11.18
+ * Scheduling and budgeting split transactions using TTs
+ *
+ * A hub can have a single TT for all its ports, or multiple TTs (one for each
+ * port).  The bandwidth and budgeting information for the full/low-speed bus
+ * below each TT is self-contained and independent of the other TTs or the
+ * high-speed bus.
+ *
+ * "Bandwidth" refers to the number of microseconds on the FS/LS bus allocated
+ * to an interrupt or isochronous endpoint for each frame.  "Budget" refers to
+ * the best-case estimate of the number of full-speed bytes allocated to an
+ * endpoint for each microframe within an allocated frame.
+ *
+ * Removal of an endpoint invalidates a TT's budget.  Instead of trying to
+ * keep an up-to-date record, we recompute the budget when it is needed.
+ */
+
+struct ehci_tt {
+       u16                     bandwidth[EHCI_BANDWIDTH_FRAMES];
+
+       struct list_head        tt_list;        /* List of all ehci_tt's */
+       struct list_head        ps_list;        /* Items using this TT */
+       struct usb_tt           *usb_tt;
+       int                     tt_port;        /* TT port number */
+};
+
+/*-------------------------------------------------------------------------*/
+
 /* Prepare the PORTSC wakeup flags during controller suspend/resume */
 
 #define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup)     \
index fce13bcc4a3e69e23eae70d3f49c512101798ed0..55486bd23cf1f7c0917e0a61794a4db3a807e828 100644 (file)
@@ -412,7 +412,7 @@ struct debug_buffer {
                        tmp = 'h'; break; \
                default:                \
                        tmp = '?'; break; \
-               }; tmp; })
+               } tmp; })
 
 static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token)
 {
index 299253c826c7f50db1f3fddca6ebe7e5b86f53e7..e1c6d850a7e1320fa38ffcd518afee2dac46b78b 100644 (file)
@@ -402,7 +402,7 @@ struct debug_buffer {
                case QH_LOW_SPEED:  tmp = 'l'; break; \
                case QH_HIGH_SPEED: tmp = 'h'; break; \
                default: tmp = '?'; break; \
-               }; tmp; })
+               } tmp; })
 
 static inline char token_mark(struct fusbh200_hcd *fusbh200, __hc32 token)
 {
index 5b86ffb88f1cb53205061a08e339c65620293a9f..ada0a52797b183507d724c25bf27f02a0d0e750c 100644 (file)
@@ -199,10 +199,14 @@ static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd)
 {
        struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
        struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
 
-       dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
-               usb_hcd, hwahc);
-       return -ENOSYS;
+       /*
+        * We cannot query the HWA for the WUSB time since that requires sending
+        * a synchronous URB and this function can be called in_interrupt.
+        * Instead, query the USB frame number for our parent and use that.
+        */
+       return usb_get_current_frame_number(wa->usb_dev);
 }
 
 static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
@@ -566,14 +570,10 @@ found:
                goto error;
        }
        wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr;
-       /* Make LE fields CPU order */
-       wa_descr->bcdWAVersion = le16_to_cpu(wa_descr->bcdWAVersion);
-       wa_descr->wNumRPipes = le16_to_cpu(wa_descr->wNumRPipes);
-       wa_descr->wRPipeMaxBlock = le16_to_cpu(wa_descr->wRPipeMaxBlock);
-       if (wa_descr->bcdWAVersion > 0x0100)
+       if (le16_to_cpu(wa_descr->bcdWAVersion) > 0x0100)
                dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n",
-                        wa_descr->bcdWAVersion & 0xff00 >> 8,
-                        wa_descr->bcdWAVersion & 0x00ff);
+                        le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00 >> 8,
+                        le16_to_cpu(wa_descr->bcdWAVersion) & 0x00ff);
        result = 0;
 error:
        return result;
@@ -679,7 +679,8 @@ static void hwahc_security_release(struct hwahc *hwahc)
        /* nothing to do here so far... */
 }
 
-static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface)
+static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface,
+       kernel_ulong_t quirks)
 {
        int result;
        struct device *dev = &iface->dev;
@@ -724,7 +725,7 @@ static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface)
                dev_err(dev, "Can't create WUSB HC structures: %d\n", result);
                goto error_wusbhc_create;
        }
-       result = wa_create(&hwahc->wa, iface);
+       result = wa_create(&hwahc->wa, iface, quirks);
        if (result < 0)
                goto error_wa_create;
        return 0;
@@ -780,7 +781,7 @@ static int hwahc_probe(struct usb_interface *usb_iface,
        wusbhc = usb_hcd_to_wusbhc(usb_hcd);
        hwahc = container_of(wusbhc, struct hwahc, wusbhc);
        hwahc_init(hwahc);
-       result = hwahc_create(hwahc, usb_iface);
+       result = hwahc_create(hwahc, usb_iface, id->driver_info);
        if (result < 0) {
                dev_err(dev, "Cannot initialize internals: %d\n", result);
                goto error_hwahc_create;
@@ -824,6 +825,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface)
 }
 
 static struct usb_device_id hwahc_id_table[] = {
+       /* Alereon 5310 */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01),
+         .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC },
+       /* Alereon 5611 */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01),
+         .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC },
        /* FIXME: use class labels for this */
        { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
        {},
index 6f29abad6815578a6282eb6e6d9cbe3b6469a8d8..935a2dd367a81fb682d820fdf017aa3b7776596f 100644 (file)
@@ -2108,7 +2108,7 @@ static int isp1362_show(struct seq_file *s, void *unused)
                                   default:
                                           s = "?";
                                           break;
-                                  };
+                                  }
                                   s;}), ep->maxpacket) ;
                list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
                        seq_printf(s, "  urb%p, %d/%d\n", urb,
index caa3764a34075e4735a9aca7d907f8fa693fde83..476b5a5baf251a0781a0843fcead65baab0f83c6 100644 (file)
  */
 
 #include <linux/clk.h>
-#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
+#include <linux/platform_device.h>
 #include <linux/platform_data/atmel.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
 
 #include <mach/hardware.h>
 #include <asm/gpio.h>
 
 #include <mach/cpu.h>
 
-#ifndef CONFIG_ARCH_AT91
-#error "CONFIG_ARCH_AT91 must be defined."
-#endif
+
+#include "ohci.h"
 
 #define valid_port(index)      ((index) >= 0 && (index) < AT91_MAX_USBH_PORTS)
 #define at91_for_each_port(index)      \
 
 /* interface, function and usb clocks; sometimes also an AHB clock */
 static struct clk *iclk, *fclk, *uclk, *hclk;
+/* interface and function clocks; sometimes also an AHB clock */
+
+#define DRIVER_DESC "OHCI Atmel driver"
+
+static const char hcd_name[] = "ohci-atmel";
+
+static struct hc_driver __read_mostly ohci_at91_hc_driver;
 static int clocked;
+static int (*orig_ohci_hub_control)(struct usb_hcd  *hcd, u16 typeReq,
+                       u16 wValue, u16 wIndex, char *buf, u16 wLength);
+static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
 
 extern int usb_disabled(void);
 
@@ -117,6 +132,8 @@ static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
 static int usb_hcd_at91_probe(const struct hc_driver *driver,
                        struct platform_device *pdev)
 {
+       struct at91_usbh_data *board;
+       struct ohci_hcd *ohci;
        int retval;
        struct usb_hcd *hcd = NULL;
 
@@ -177,8 +194,10 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
                }
        }
 
+       board = hcd->self.controller->platform_data;
+       ohci = hcd_to_ohci(hcd);
+       ohci->num_ports = board->ports;
        at91_start_hc(pdev);
-       ohci_hcd_init(hcd_to_ohci(hcd));
 
        retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
        if (retval == 0)
@@ -238,36 +257,6 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd,
 }
 
 /*-------------------------------------------------------------------------*/
-
-static int
-ohci_at91_reset (struct usb_hcd *hcd)
-{
-       struct at91_usbh_data   *board = dev_get_platdata(hcd->self.controller);
-       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
-       int                     ret;
-
-       if ((ret = ohci_init(ohci)) < 0)
-               return ret;
-
-       ohci->num_ports = board->ports;
-       return 0;
-}
-
-static int
-ohci_at91_start (struct usb_hcd *hcd)
-{
-       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
-       int                     ret;
-
-       if ((ret = ohci_run(ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start %s\n",
-                       hcd->self.bus_name);
-               ohci_stop(hcd);
-               return ret;
-       }
-       return 0;
-}
-
 static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable)
 {
        if (!valid_port(port))
@@ -297,8 +286,8 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
  */
 static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)
 {
-       struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller);
-       int length = ohci_hub_status_data(hcd, buf);
+       struct at91_usbh_data *pdata = hcd->self.controller->platform_data;
+       int length = orig_ohci_hub_status_data(hcd, buf);
        int port;
 
        at91_for_each_port(port) {
@@ -376,7 +365,8 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                break;
        }
 
-       ret = ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, buf, wLength);
+       ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex + 1,
+                               buf, wLength);
        if (ret)
                goto out;
 
@@ -430,51 +420,6 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
 /*-------------------------------------------------------------------------*/
 
-static const struct hc_driver ohci_at91_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "AT91 OHCI",
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset =                ohci_at91_reset,
-       .start =                ohci_at91_start,
-       .stop =                 ohci_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_at91_hub_status_data,
-       .hub_control =          ohci_at91_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
-
 static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
 {
        struct platform_device *pdev = data;
@@ -703,7 +648,11 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
         * REVISIT: some boards will be able to turn VBUS off...
         */
        if (at91_suspend_entering_slow_clock()) {
-               ohci_usb_reset (ohci);
+               ohci->hc_control = ohci_readl(ohci, &ohci->regs->control);
+               ohci->hc_control &= OHCI_CTRL_RWC;
+               ohci_writel(ohci, ohci->hc_control, &ohci->regs->control);
+               ohci->rh_state = OHCI_RH_HALTED;
+
                /* flush the writes */
                (void) ohci_readl (ohci, &ohci->regs->control);
                at91_stop_clock();
@@ -730,8 +679,6 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
 #define ohci_hcd_at91_drv_resume  NULL
 #endif
 
-MODULE_ALIAS("platform:at91_ohci");
-
 static struct platform_driver ohci_hcd_at91_driver = {
        .probe          = ohci_hcd_at91_drv_probe,
        .remove         = ohci_hcd_at91_drv_remove,
@@ -744,3 +691,40 @@ static struct platform_driver ohci_hcd_at91_driver = {
                .of_match_table = of_match_ptr(at91_ohci_dt_ids),
        },
 };
+
+static int __init ohci_at91_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ohci_init_driver(&ohci_at91_hc_driver, NULL);
+
+       /*
+        * The Atmel HW has some unusual quirks, which require Atmel-specific
+        * workarounds. We override certain hc_driver functions here to
+        * achieve that. We explicitly do not enhance ohci_driver_overrides to
+        * allow this more easily, since this is an unusual case, and we don't
+        * want to encourage others to override these functions by making it
+        * too easy.
+        */
+
+       orig_ohci_hub_control = ohci_at91_hc_driver.hub_control;
+       orig_ohci_hub_status_data = ohci_at91_hc_driver.hub_status_data;
+
+       ohci_at91_hc_driver.hub_status_data     = ohci_at91_hub_status_data;
+       ohci_at91_hc_driver.hub_control         = ohci_at91_hub_control;
+
+       return platform_driver_register(&ohci_hcd_at91_driver);
+}
+module_init(ohci_at91_init);
+
+static void __exit ohci_at91_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_at91_driver);
+}
+module_exit(ohci_at91_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_ohci");
index 31b81f9eacdc90cff804bcb89d4ecefff9a3a16f..3fca52ec02ac0b2d0859e96699958030938fce06 100644 (file)
@@ -17,7 +17,7 @@
        case PIPE_BULK:         temp = "bulk"; break; \
        case PIPE_INTERRUPT:    temp = "intr"; break; \
        default:                temp = "isoc"; break; \
-       }; temp;})
+       } temp;})
 #define pipestring(pipe) edstring(usb_pipetype(pipe))
 
 /* debug| print the main components of an URB
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
deleted file mode 100644 (file)
index 84a20d5..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * OHCI HCD (Host Controller Driver) for USB.
- *
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
- * (C) Copyright 2002 Hewlett-Packard Company
- *
- * Bus Glue for ep93xx.
- *
- * Written by Christopher Hoover <ch@hpl.hp.com>
- * Based on fragments of previous driver by Russell King et al.
- *
- * Modified for LH7A404 from ohci-sa1111.c
- *  by Durgesh Pattamatta <pattamattad@sharpsec.com>
- *
- * Modified for pxa27x from ohci-lh7a404.c
- *  by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
- *
- * Modified for ep93xx from ohci-pxa27x.c
- *  by Lennert Buytenhek <buytenh@wantstofly.org> 28-2-2006
- *  Based on an earlier driver by Ray Lehtiniemi
- *
- * This file is licenced under the GPL.
- */
-
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/signal.h>
-#include <linux/platform_device.h>
-
-static struct clk *usb_host_clock;
-
-static int ohci_ep93xx_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       if ((ret = ohci_init(ohci)) < 0)
-               return ret;
-
-       if ((ret = ohci_run(ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start %s\n",
-                       hcd->self.bus_name);
-               ohci_stop(hcd);
-               return ret;
-       }
-
-       return 0;
-}
-
-static struct hc_driver ohci_ep93xx_hc_driver = {
-       .description            = hcd_name,
-       .product_desc           = "EP93xx OHCI",
-       .hcd_priv_size          = sizeof(struct ohci_hcd),
-       .irq                    = ohci_irq,
-       .flags                  = HCD_USB11 | HCD_MEMORY,
-       .start                  = ohci_ep93xx_start,
-       .stop                   = ohci_stop,
-       .shutdown               = ohci_shutdown,
-       .urb_enqueue            = ohci_urb_enqueue,
-       .urb_dequeue            = ohci_urb_dequeue,
-       .endpoint_disable       = ohci_endpoint_disable,
-       .get_frame_number       = ohci_get_frame,
-       .hub_status_data        = ohci_hub_status_data,
-       .hub_control            = ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend            = ohci_bus_suspend,
-       .bus_resume             = ohci_bus_resume,
-#endif
-       .start_port_reset       = ohci_start_port_reset,
-};
-
-static int ohci_hcd_ep93xx_drv_probe(struct platform_device *pdev)
-{
-       struct usb_hcd *hcd;
-       struct resource *res;
-       int irq;
-       int ret;
-
-       if (usb_disabled())
-               return -ENODEV;
-
-       irq = platform_get_irq(pdev, 0);
-       if (irq < 0)
-               return irq;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENXIO;
-
-       hcd = usb_create_hcd(&ohci_ep93xx_hc_driver, &pdev->dev, "ep93xx");
-       if (!hcd)
-               return -ENOMEM;
-
-       hcd->rsrc_start = res->start;
-       hcd->rsrc_len = resource_size(res);
-
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(hcd->regs)) {
-               ret = PTR_ERR(hcd->regs);
-               goto err_put_hcd;
-       }
-
-       usb_host_clock = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(usb_host_clock)) {
-               ret = PTR_ERR(usb_host_clock);
-               goto err_put_hcd;
-       }
-
-       clk_enable(usb_host_clock);
-
-       ohci_hcd_init(hcd_to_ohci(hcd));
-
-       ret = usb_add_hcd(hcd, irq, 0);
-       if (ret)
-               goto err_clk_disable;
-
-       return 0;
-
-err_clk_disable:
-       clk_disable(usb_host_clock);
-err_put_hcd:
-       usb_put_hcd(hcd);
-
-       return ret;
-}
-
-static int ohci_hcd_ep93xx_drv_remove(struct platform_device *pdev)
-{
-       struct usb_hcd *hcd = platform_get_drvdata(pdev);
-
-       usb_remove_hcd(hcd);
-       clk_disable(usb_host_clock);
-       usb_put_hcd(hcd);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int ohci_hcd_ep93xx_drv_suspend(struct platform_device *pdev, pm_message_t state)
-{
-       struct usb_hcd *hcd = platform_get_drvdata(pdev);
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-
-       if (time_before(jiffies, ohci->next_statechange))
-               msleep(5);
-       ohci->next_statechange = jiffies;
-
-       clk_disable(usb_host_clock);
-       return 0;
-}
-
-static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev)
-{
-       struct usb_hcd *hcd = platform_get_drvdata(pdev);
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-
-       if (time_before(jiffies, ohci->next_statechange))
-               msleep(5);
-       ohci->next_statechange = jiffies;
-
-       clk_enable(usb_host_clock);
-
-       ohci_resume(hcd, false);
-       return 0;
-}
-#endif
-
-
-static struct platform_driver ohci_hcd_ep93xx_driver = {
-       .probe          = ohci_hcd_ep93xx_drv_probe,
-       .remove         = ohci_hcd_ep93xx_drv_remove,
-       .shutdown       = usb_hcd_platform_shutdown,
-#ifdef CONFIG_PM
-       .suspend        = ohci_hcd_ep93xx_drv_suspend,
-       .resume         = ohci_hcd_ep93xx_drv_resume,
-#endif
-       .driver         = {
-               .name   = "ep93xx-ohci",
-               .owner  = THIS_MODULE,
-       },
-};
-
-MODULE_ALIAS("platform:ep93xx-ohci");
index dc6ee9adacf58679305df6baa851167e34e61ed4..a87baedc0aa79555bcb60ed72a27036fc757566f 100644 (file)
  */
 
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
-#include <linux/platform_data/usb-ohci-exynos.h>
 #include <linux/usb/phy.h>
 #include <linux/usb/samsung_usb_phy.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI EXYNOS driver"
+
+static const char hcd_name[] = "ohci-exynos";
+static struct hc_driver __read_mostly exynos_ohci_hc_driver;
+
+#define to_exynos_ohci(hcd) (struct exynos_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
 
 struct exynos_ohci_hcd {
-       struct device *dev;
-       struct usb_hcd *hcd;
        struct clk *clk;
        struct usb_phy *phy;
        struct usb_otg *otg;
-       struct exynos4_ohci_platdata *pdata;
 };
 
-static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci)
+static void exynos_ohci_phy_enable(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(exynos_ohci->dev);
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
 
        if (exynos_ohci->phy)
                usb_phy_init(exynos_ohci->phy);
-       else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_init)
-               exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
 }
 
-static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci)
+static void exynos_ohci_phy_disable(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(exynos_ohci->dev);
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
 
        if (exynos_ohci->phy)
                usb_phy_shutdown(exynos_ohci->phy);
-       else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_exit)
-               exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
-}
-
-static int ohci_exynos_reset(struct usb_hcd *hcd)
-{
-       return ohci_init(hcd_to_ohci(hcd));
 }
 
-static int ohci_exynos_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       ohci_dbg(ohci, "ohci_exynos_start, ohci:%p", ohci);
-
-       ret = ohci_run(ohci);
-       if (ret < 0) {
-               dev_err(hcd->self.controller, "can't start %s\n",
-                       hcd->self.bus_name);
-               ohci_stop(hcd);
-               return ret;
-       }
-
-       return 0;
-}
-
-static const struct hc_driver exynos_ohci_hc_driver = {
-       .description            = hcd_name,
-       .product_desc           = "EXYNOS OHCI Host Controller",
-       .hcd_priv_size          = sizeof(struct ohci_hcd),
-
-       .irq                    = ohci_irq,
-       .flags                  = HCD_MEMORY|HCD_USB11,
-
-       .reset                  = ohci_exynos_reset,
-       .start                  = ohci_exynos_start,
-       .stop                   = ohci_stop,
-       .shutdown               = ohci_shutdown,
-
-       .get_frame_number       = ohci_get_frame,
-
-       .urb_enqueue            = ohci_urb_enqueue,
-       .urb_dequeue            = ohci_urb_dequeue,
-       .endpoint_disable       = ohci_endpoint_disable,
-
-       .hub_status_data        = ohci_hub_status_data,
-       .hub_control            = ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend            = ohci_bus_suspend,
-       .bus_resume             = ohci_bus_resume,
-#endif
-       .start_port_reset       = ohci_start_port_reset,
-};
-
 static int exynos_ohci_probe(struct platform_device *pdev)
 {
-       struct exynos4_ohci_platdata *pdata = dev_get_platdata(&pdev->dev);
        struct exynos_ohci_hcd *exynos_ohci;
        struct usb_hcd *hcd;
-       struct ohci_hcd *ohci;
        struct resource *res;
        struct usb_phy *phy;
        int irq;
@@ -119,10 +76,14 @@ static int exynos_ohci_probe(struct platform_device *pdev)
        if (!pdev->dev.coherent_dma_mask)
                pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
 
-       exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd),
-                                       GFP_KERNEL);
-       if (!exynos_ohci)
+       hcd = usb_create_hcd(&exynos_ohci_hc_driver,
+                               &pdev->dev, dev_name(&pdev->dev));
+       if (!hcd) {
+               dev_err(&pdev->dev, "Unable to create HCD\n");
                return -ENOMEM;
+       }
+
+       exynos_ohci = to_exynos_ohci(hcd);
 
        if (of_device_is_compatible(pdev->dev.of_node,
                                        "samsung,exynos5440-ohci"))
@@ -130,30 +91,15 @@ static int exynos_ohci_probe(struct platform_device *pdev)
 
        phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
        if (IS_ERR(phy)) {
-               /* Fallback to pdata */
-               if (!pdata) {
-                       dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
-                       return -EPROBE_DEFER;
-               } else {
-                       exynos_ohci->pdata = pdata;
-               }
+               usb_put_hcd(hcd);
+               dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
+               return -EPROBE_DEFER;
        } else {
                exynos_ohci->phy = phy;
                exynos_ohci->otg = phy->otg;
        }
 
 skip_phy:
-
-       exynos_ohci->dev = &pdev->dev;
-
-       hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev,
-                                       dev_name(&pdev->dev));
-       if (!hcd) {
-               dev_err(&pdev->dev, "Unable to create HCD\n");
-               return -ENOMEM;
-       }
-
-       exynos_ohci->hcd = hcd;
        exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
 
        if (IS_ERR(exynos_ohci->clk)) {
@@ -190,26 +136,21 @@ skip_phy:
        }
 
        if (exynos_ohci->otg)
-               exynos_ohci->otg->set_host(exynos_ohci->otg,
-                                       &exynos_ohci->hcd->self);
+               exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
 
-       exynos_ohci_phy_enable(exynos_ohci);
+       platform_set_drvdata(pdev, hcd);
 
-       ohci = hcd_to_ohci(hcd);
-       ohci_hcd_init(ohci);
+       exynos_ohci_phy_enable(pdev);
 
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err) {
                dev_err(&pdev->dev, "Failed to add USB HCD\n");
                goto fail_add_hcd;
        }
-
-       platform_set_drvdata(pdev, exynos_ohci);
-
        return 0;
 
 fail_add_hcd:
-       exynos_ohci_phy_disable(exynos_ohci);
+       exynos_ohci_phy_disable(pdev);
 fail_io:
        clk_disable_unprepare(exynos_ohci->clk);
 fail_clk:
@@ -219,16 +160,15 @@ fail_clk:
 
 static int exynos_ohci_remove(struct platform_device *pdev)
 {
-       struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
-       struct usb_hcd *hcd = exynos_ohci->hcd;
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
 
        usb_remove_hcd(hcd);
 
        if (exynos_ohci->otg)
-               exynos_ohci->otg->set_host(exynos_ohci->otg,
-                                       &exynos_ohci->hcd->self);
+               exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
 
-       exynos_ohci_phy_disable(exynos_ohci);
+       exynos_ohci_phy_disable(pdev);
 
        clk_disable_unprepare(exynos_ohci->clk);
 
@@ -239,8 +179,7 @@ static int exynos_ohci_remove(struct platform_device *pdev)
 
 static void exynos_ohci_shutdown(struct platform_device *pdev)
 {
-       struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
-       struct usb_hcd *hcd = exynos_ohci->hcd;
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
        if (hcd->driver->shutdown)
                hcd->driver->shutdown(hcd);
@@ -249,9 +188,10 @@ static void exynos_ohci_shutdown(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int exynos_ohci_suspend(struct device *dev)
 {
-       struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
-       struct usb_hcd *hcd = exynos_ohci->hcd;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       struct platform_device *pdev = to_platform_device(dev);
        unsigned long flags;
        int rc = 0;
 
@@ -271,10 +211,9 @@ static int exynos_ohci_suspend(struct device *dev)
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
 
        if (exynos_ohci->otg)
-               exynos_ohci->otg->set_host(exynos_ohci->otg,
-                                       &exynos_ohci->hcd->self);
+               exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
 
-       exynos_ohci_phy_disable(exynos_ohci);
+       exynos_ohci_phy_disable(pdev);
 
        clk_disable_unprepare(exynos_ohci->clk);
 
@@ -286,16 +225,16 @@ fail:
 
 static int exynos_ohci_resume(struct device *dev)
 {
-       struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
-       struct usb_hcd *hcd = exynos_ohci->hcd;
+       struct usb_hcd *hcd                     = dev_get_drvdata(dev);
+       struct exynos_ohci_hcd *exynos_ohci     = to_exynos_ohci(hcd);
+       struct platform_device *pdev            = to_platform_device(dev);
 
        clk_prepare_enable(exynos_ohci->clk);
 
        if (exynos_ohci->otg)
-               exynos_ohci->otg->set_host(exynos_ohci->otg,
-                                       &exynos_ohci->hcd->self);
+               exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
 
-       exynos_ohci_phy_enable(exynos_ohci);
+       exynos_ohci_phy_enable(pdev);
 
        ohci_resume(hcd, false);
 
@@ -306,6 +245,10 @@ static int exynos_ohci_resume(struct device *dev)
 #define exynos_ohci_resume     NULL
 #endif
 
+static const struct ohci_driver_overrides exynos_overrides __initconst = {
+       .extra_priv_size =      sizeof(struct exynos_ohci_hcd),
+};
+
 static const struct dev_pm_ops exynos_ohci_pm_ops = {
        .suspend        = exynos_ohci_suspend,
        .resume         = exynos_ohci_resume,
@@ -331,6 +274,23 @@ static struct platform_driver exynos_ohci_driver = {
                .of_match_table = of_match_ptr(exynos_ohci_match),
        }
 };
+static int __init ohci_exynos_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ohci_init_driver(&exynos_ohci_hc_driver, &exynos_overrides);
+       return platform_driver_register(&exynos_ohci_driver);
+}
+module_init(ohci_exynos_init);
+
+static void __exit ohci_exynos_cleanup(void)
+{
+       platform_driver_unregister(&exynos_ohci_driver);
+}
+module_exit(ohci_exynos_cleanup);
 
 MODULE_ALIAS("platform:exynos-ohci");
 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_LICENSE("GPL v2");
index 604cad1bcf9cd984666aaf0b2b797b48ee6e3382..8ada13f8dde2c7350977cf343987fbb14c2f303b 100644 (file)
@@ -1161,10 +1161,12 @@ void ohci_init_driver(struct hc_driver *drv,
        /* Copy the generic table to drv and then apply the overrides */
        *drv = ohci_hc_driver;
 
-       drv->product_desc = over->product_desc;
-       drv->hcd_priv_size += over->extra_priv_size;
-       if (over->reset)
-               drv->reset = over->reset;
+       if (over) {
+               drv->product_desc = over->product_desc;
+               drv->hcd_priv_size += over->extra_priv_size;
+               if (over->reset)
+                       drv->reset = over->reset;
+       }
 }
 EXPORT_SYMBOL_GPL(ohci_init_driver);
 
@@ -1179,46 +1181,6 @@ MODULE_LICENSE ("GPL");
 #define SA1111_DRIVER          ohci_hcd_sa1111_driver
 #endif
 
-#if defined(CONFIG_ARCH_S3C24XX) || defined(CONFIG_ARCH_S3C64XX)
-#include "ohci-s3c2410.c"
-#define S3C2410_PLATFORM_DRIVER        ohci_hcd_s3c2410_driver
-#endif
-
-#ifdef CONFIG_USB_OHCI_EXYNOS
-#include "ohci-exynos.c"
-#define EXYNOS_PLATFORM_DRIVER exynos_ohci_driver
-#endif
-
-#ifdef CONFIG_USB_OHCI_HCD_OMAP1
-#include "ohci-omap.c"
-#define OMAP1_PLATFORM_DRIVER  ohci_hcd_omap_driver
-#endif
-
-#ifdef CONFIG_USB_OHCI_HCD_OMAP3
-#include "ohci-omap3.c"
-#define OMAP3_PLATFORM_DRIVER  ohci_hcd_omap3_driver
-#endif
-
-#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
-#include "ohci-pxa27x.c"
-#define PLATFORM_DRIVER                ohci_hcd_pxa27x_driver
-#endif
-
-#ifdef CONFIG_ARCH_EP93XX
-#include "ohci-ep93xx.c"
-#define EP93XX_PLATFORM_DRIVER ohci_hcd_ep93xx_driver
-#endif
-
-#ifdef CONFIG_ARCH_AT91
-#include "ohci-at91.c"
-#define AT91_PLATFORM_DRIVER   ohci_hcd_at91_driver
-#endif
-
-#ifdef CONFIG_ARCH_LPC32XX
-#include "ohci-nxp.c"
-#define NXP_PLATFORM_DRIVER    usb_hcd_nxp_driver
-#endif
-
 #ifdef CONFIG_ARCH_DAVINCI_DA8XX
 #include "ohci-da8xx.c"
 #define DAVINCI_PLATFORM_DRIVER        ohci_hcd_da8xx_driver
@@ -1229,11 +1191,6 @@ MODULE_LICENSE ("GPL");
 #define OF_PLATFORM_DRIVER     ohci_hcd_ppc_of_driver
 #endif
 
-#ifdef CONFIG_PLAT_SPEAR
-#include "ohci-spear.c"
-#define SPEAR_PLATFORM_DRIVER  spear_ohci_hcd_driver
-#endif
-
 #ifdef CONFIG_PPC_PS3
 #include "ohci-ps3.c"
 #define PS3_SYSTEM_BUS_DRIVER  ps3_ohci_driver
@@ -1296,18 +1253,6 @@ static int __init ohci_hcd_mod_init(void)
                goto error_platform;
 #endif
 
-#ifdef OMAP1_PLATFORM_DRIVER
-       retval = platform_driver_register(&OMAP1_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_omap1_platform;
-#endif
-
-#ifdef OMAP3_PLATFORM_DRIVER
-       retval = platform_driver_register(&OMAP3_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_omap3_platform;
-#endif
-
 #ifdef OF_PLATFORM_DRIVER
        retval = platform_driver_register(&OF_PLATFORM_DRIVER);
        if (retval < 0)
@@ -1332,79 +1277,19 @@ static int __init ohci_hcd_mod_init(void)
                goto error_tmio;
 #endif
 
-#ifdef S3C2410_PLATFORM_DRIVER
-       retval = platform_driver_register(&S3C2410_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_s3c2410;
-#endif
-
-#ifdef EXYNOS_PLATFORM_DRIVER
-       retval = platform_driver_register(&EXYNOS_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_exynos;
-#endif
-
-#ifdef EP93XX_PLATFORM_DRIVER
-       retval = platform_driver_register(&EP93XX_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_ep93xx;
-#endif
-
-#ifdef AT91_PLATFORM_DRIVER
-       retval = platform_driver_register(&AT91_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_at91;
-#endif
-
-#ifdef NXP_PLATFORM_DRIVER
-       retval = platform_driver_register(&NXP_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_nxp;
-#endif
-
 #ifdef DAVINCI_PLATFORM_DRIVER
        retval = platform_driver_register(&DAVINCI_PLATFORM_DRIVER);
        if (retval < 0)
                goto error_davinci;
 #endif
 
-#ifdef SPEAR_PLATFORM_DRIVER
-       retval = platform_driver_register(&SPEAR_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_spear;
-#endif
-
        return retval;
 
        /* Error path */
-#ifdef SPEAR_PLATFORM_DRIVER
-       platform_driver_unregister(&SPEAR_PLATFORM_DRIVER);
- error_spear:
-#endif
 #ifdef DAVINCI_PLATFORM_DRIVER
        platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER);
  error_davinci:
 #endif
-#ifdef NXP_PLATFORM_DRIVER
-       platform_driver_unregister(&NXP_PLATFORM_DRIVER);
- error_nxp:
-#endif
-#ifdef AT91_PLATFORM_DRIVER
-       platform_driver_unregister(&AT91_PLATFORM_DRIVER);
- error_at91:
-#endif
-#ifdef EP93XX_PLATFORM_DRIVER
-       platform_driver_unregister(&EP93XX_PLATFORM_DRIVER);
- error_ep93xx:
-#endif
-#ifdef EXYNOS_PLATFORM_DRIVER
-       platform_driver_unregister(&EXYNOS_PLATFORM_DRIVER);
- error_exynos:
-#endif
-#ifdef S3C2410_PLATFORM_DRIVER
-       platform_driver_unregister(&S3C2410_PLATFORM_DRIVER);
- error_s3c2410:
-#endif
 #ifdef TMIO_OHCI_DRIVER
        platform_driver_unregister(&TMIO_OHCI_DRIVER);
  error_tmio:
@@ -1421,14 +1306,6 @@ static int __init ohci_hcd_mod_init(void)
        platform_driver_unregister(&OF_PLATFORM_DRIVER);
  error_of_platform:
 #endif
-#ifdef OMAP3_PLATFORM_DRIVER
-       platform_driver_unregister(&OMAP3_PLATFORM_DRIVER);
- error_omap3_platform:
-#endif
-#ifdef OMAP1_PLATFORM_DRIVER
-       platform_driver_unregister(&OMAP1_PLATFORM_DRIVER);
- error_omap1_platform:
-#endif
 #ifdef PLATFORM_DRIVER
        platform_driver_unregister(&PLATFORM_DRIVER);
  error_platform:
@@ -1450,27 +1327,9 @@ module_init(ohci_hcd_mod_init);
 
 static void __exit ohci_hcd_mod_exit(void)
 {
-#ifdef SPEAR_PLATFORM_DRIVER
-       platform_driver_unregister(&SPEAR_PLATFORM_DRIVER);
-#endif
 #ifdef DAVINCI_PLATFORM_DRIVER
        platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER);
 #endif
-#ifdef NXP_PLATFORM_DRIVER
-       platform_driver_unregister(&NXP_PLATFORM_DRIVER);
-#endif
-#ifdef AT91_PLATFORM_DRIVER
-       platform_driver_unregister(&AT91_PLATFORM_DRIVER);
-#endif
-#ifdef EP93XX_PLATFORM_DRIVER
-       platform_driver_unregister(&EP93XX_PLATFORM_DRIVER);
-#endif
-#ifdef EXYNOS_PLATFORM_DRIVER
-       platform_driver_unregister(&EXYNOS_PLATFORM_DRIVER);
-#endif
-#ifdef S3C2410_PLATFORM_DRIVER
-       platform_driver_unregister(&S3C2410_PLATFORM_DRIVER);
-#endif
 #ifdef TMIO_OHCI_DRIVER
        platform_driver_unregister(&TMIO_OHCI_DRIVER);
 #endif
@@ -1483,12 +1342,6 @@ static void __exit ohci_hcd_mod_exit(void)
 #ifdef OF_PLATFORM_DRIVER
        platform_driver_unregister(&OF_PLATFORM_DRIVER);
 #endif
-#ifdef OMAP3_PLATFORM_DRIVER
-       platform_driver_unregister(&OMAP3_PLATFORM_DRIVER);
-#endif
-#ifdef OMAP1_PLATFORM_DRIVER
-       platform_driver_unregister(&OMAP1_PLATFORM_DRIVER);
-#endif
 #ifdef PLATFORM_DRIVER
        platform_driver_unregister(&PLATFORM_DRIVER);
 #endif
index 2347ab83f046f7c7ce561aaec5562f47399efd59..61705a760e7db5d68fed2df19f94b3bdef96697e 100644 (file)
@@ -212,10 +212,11 @@ __acquires(ohci->lock)
        /* Sometimes PCI D3 suspend trashes frame timings ... */
        periodic_reinit (ohci);
 
-       /* the following code is executed with ohci->lock held and
-        * irqs disabled if and only if autostopped is true
+       /*
+        * The following code is executed with ohci->lock held and
+        * irqs disabled if and only if autostopped is true.  This
+        * will cause sparse to warn about a "context imbalance".
         */
-
 skip_resume:
        /* interrupts might have been disabled */
        ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
@@ -531,7 +532,7 @@ ohci_hub_descriptor (
            temp |= 0x0010;
        else if (rh & RH_A_OCPM)        /* per-port overcurrent reporting? */
            temp |= 0x0008;
-       desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp);
+       desc->wHubCharacteristics = cpu_to_le16(temp);
 
        /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
        rh = roothub_b (ohci);
index 7d7d507d54e83ef89cdd0695d694f7e3bb4a5e7d..9ab7e24ba65d9f999623a0c65f01021e5bc0d36b 100644 (file)
  * or implied.
  */
 #include <linux/clk.h>
-#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
 #include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/usb/isp1301.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 #define start_int_umask(irq)
 #endif
 
+#define DRIVER_DESC "OHCI NXP driver"
+
+static const char hcd_name[] = "ohci-nxp";
+static struct hc_driver __read_mostly ohci_nxp_hc_driver;
+
 static struct i2c_client *isp1301_i2c_client;
 
 extern int usb_disabled(void);
@@ -132,14 +146,14 @@ static inline void isp1301_vbus_off(void)
                OTG1_VBUS_DRV);
 }
 
-static void nxp_start_hc(void)
+static void ohci_nxp_start_hc(void)
 {
        unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN;
        __raw_writel(tmp, USB_OTG_STAT_CONTROL);
        isp1301_vbus_on();
 }
 
-static void nxp_stop_hc(void)
+static void ohci_nxp_stop_hc(void)
 {
        unsigned long tmp;
        isp1301_vbus_off();
@@ -147,68 +161,9 @@ static void nxp_stop_hc(void)
        __raw_writel(tmp, USB_OTG_STAT_CONTROL);
 }
 
-static int ohci_nxp_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       if ((ret = ohci_init(ohci)) < 0)
-               return ret;
-
-       if ((ret = ohci_run(ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start\n");
-               ohci_stop(hcd);
-               return ret;
-       }
-       return 0;
-}
-
-static const struct hc_driver ohci_nxp_hc_driver = {
-       .description = hcd_name,
-       .product_desc =         "nxp OHCI",
-
-       /*
-        * generic hardware linkage
-        */
-       .irq = ohci_irq,
-       .flags = HCD_USB11 | HCD_MEMORY,
-
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-       /*
-        * basic lifecycle operations
-        */
-       .start = ohci_nxp_start,
-       .stop = ohci_stop,
-       .shutdown = ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue = ohci_urb_enqueue,
-       .urb_dequeue = ohci_urb_dequeue,
-       .endpoint_disable = ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number = ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data = ohci_hub_status_data,
-       .hub_control = ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend = ohci_bus_suspend,
-       .bus_resume = ohci_bus_resume,
-#endif
-       .start_port_reset = ohci_start_port_reset,
-};
-
-static int usb_hcd_nxp_probe(struct platform_device *pdev)
+static int ohci_hcd_nxp_probe(struct platform_device *pdev)
 {
        struct usb_hcd *hcd = 0;
-       struct ohci_hcd *ohci;
        const struct hc_driver *driver = &ohci_nxp_hc_driver;
        struct resource *res;
        int ret = 0, irq;
@@ -313,17 +268,15 @@ static int usb_hcd_nxp_probe(struct platform_device *pdev)
                goto fail_resource;
        }
 
-       nxp_start_hc();
+       ohci_nxp_start_hc();
        platform_set_drvdata(pdev, hcd);
-       ohci = hcd_to_ohci(hcd);
-       ohci_hcd_init(ohci);
 
        dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
        ret = usb_add_hcd(hcd, irq, 0);
        if (ret == 0)
                return ret;
 
-       nxp_stop_hc();
+       ohci_nxp_stop_hc();
 fail_resource:
        usb_put_hcd(hcd);
 fail_hcd:
@@ -345,12 +298,12 @@ fail_disable:
        return ret;
 }
 
-static int usb_hcd_nxp_remove(struct platform_device *pdev)
+static int ohci_hcd_nxp_remove(struct platform_device *pdev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
        usb_remove_hcd(hcd);
-       nxp_stop_hc();
+       ohci_nxp_stop_hc();
        usb_put_hcd(hcd);
        clk_disable(usb_pll_clk);
        clk_put(usb_pll_clk);
@@ -366,20 +319,40 @@ static int usb_hcd_nxp_remove(struct platform_device *pdev)
 MODULE_ALIAS("platform:usb-ohci");
 
 #ifdef CONFIG_OF
-static const struct of_device_id usb_hcd_nxp_match[] = {
+static const struct of_device_id ohci_hcd_nxp_match[] = {
        { .compatible = "nxp,ohci-nxp" },
        {},
 };
-MODULE_DEVICE_TABLE(of, usb_hcd_nxp_match);
+MODULE_DEVICE_TABLE(of, ohci_hcd_nxp_match);
 #endif
 
-static struct platform_driver usb_hcd_nxp_driver = {
+static struct platform_driver ohci_hcd_nxp_driver = {
        .driver = {
                .name = "usb-ohci",
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(usb_hcd_nxp_match),
+               .of_match_table = of_match_ptr(ohci_hcd_nxp_match),
        },
-       .probe = usb_hcd_nxp_probe,
-       .remove = usb_hcd_nxp_remove,
+       .probe = ohci_hcd_nxp_probe,
+       .remove = ohci_hcd_nxp_remove,
 };
 
+static int __init ohci_nxp_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_nxp_hc_driver, NULL);
+       return platform_driver_register(&ohci_hcd_nxp_driver);
+}
+module_init(ohci_nxp_init);
+
+static void __exit ohci_nxp_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_nxp_driver);
+}
+module_exit(ohci_nxp_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
index 31d3a12eb4867e56872f71cbea9f61ef1431dba5..f253214741ba014c35e28fa498894f8c5cac7847 100644 (file)
  * This file is licenced under the GPL.
  */
 
-#include <linux/signal.h>
-#include <linux/jiffies.h>
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/otg.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
 
 #include <asm/io.h>
 #include <asm/mach-types.h>
 #define OMAP1510_LB_MMU_RAM_H  0xfffec234
 #define OMAP1510_LB_MMU_RAM_L  0xfffec238
 
-
-#ifndef CONFIG_ARCH_OMAP
-#error "This file is OMAP bus glue.  CONFIG_OMAP must be defined."
-#endif
+#define DRIVER_DESC "OHCI OMAP driver"
 
 #ifdef CONFIG_TPS65010
 #include <linux/i2c/tps65010.h>
@@ -68,8 +74,9 @@ extern int ocpi_enable(void);
 
 static struct clk *usb_host_ck;
 static struct clk *usb_dc_ck;
-static int host_enabled;
-static int host_initialized;
+
+static const char hcd_name[] = "ohci-omap";
+static struct hc_driver __read_mostly ohci_omap_hc_driver;
 
 static void omap_ohci_clock_power(int on)
 {
@@ -188,7 +195,7 @@ static void start_hnp(struct ohci_hcd *ohci)
 
 /*-------------------------------------------------------------------------*/
 
-static int ohci_omap_init(struct usb_hcd *hcd)
+static int ohci_omap_reset(struct usb_hcd *hcd)
 {
        struct ohci_hcd         *ohci = hcd_to_ohci(hcd);
        struct omap_usb_config  *config = dev_get_platdata(hcd->self.controller);
@@ -198,9 +205,9 @@ static int ohci_omap_init(struct usb_hcd *hcd)
        dev_dbg(hcd->self.controller, "starting USB Controller\n");
 
        if (config->otg) {
-               ohci_to_hcd(ohci)->self.otg_port = config->otg;
+               hcd->self.otg_port = config->otg;
                /* default/minimum OTG power budget:  8 mA */
-               ohci_to_hcd(ohci)->power_budget = 8;
+               hcd->power_budget = 8;
        }
 
        /* boards can use OTG transceivers in non-OTG modes */
@@ -238,9 +245,15 @@ static int ohci_omap_init(struct usb_hcd *hcd)
                omap_1510_local_bus_init();
        }
 
-       if ((ret = ohci_init(ohci)) < 0)
+       ret = ohci_setup(hcd);
+       if (ret < 0)
                return ret;
 
+       if (config->otg || config->rwc) {
+               ohci->hc_control = OHCI_CTRL_RWC;
+               writel(OHCI_CTRL_RWC, &ohci->regs->control);
+       }
+
        /* board-specific power switching and overcurrent support */
        if (machine_is_omap_osk() || machine_is_omap_innovator()) {
                u32     rh = roothub_a (ohci);
@@ -281,14 +294,6 @@ static int ohci_omap_init(struct usb_hcd *hcd)
        return 0;
 }
 
-static void ohci_omap_stop(struct usb_hcd *hcd)
-{
-       dev_dbg(hcd->self.controller, "stopping USB Controller\n");
-       ohci_stop(hcd);
-       omap_ohci_clock_power(0);
-}
-
-
 /*-------------------------------------------------------------------------*/
 
 /**
@@ -304,7 +309,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
 {
        int retval, irq;
        struct usb_hcd *hcd = 0;
-       struct ohci_hcd *ohci;
 
        if (pdev->num_resources != 2) {
                printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
@@ -354,12 +358,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
                goto err2;
        }
 
-       ohci = hcd_to_ohci(hcd);
-       ohci_hcd_init(ohci);
-
-       host_initialized = 0;
-       host_enabled = 1;
-
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                retval = -ENXIO;
@@ -369,11 +367,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
        if (retval)
                goto err3;
 
-       host_initialized = 1;
-
-       if (!host_enabled)
-               omap_ohci_clock_power(0);
-
        return 0;
 err3:
        iounmap(hcd->regs);
@@ -402,7 +395,9 @@ err0:
 static inline void
 usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
 {
+       dev_dbg(hcd->self.controller, "stopping USB Controller\n");
        usb_remove_hcd(hcd);
+       omap_ohci_clock_power(0);
        if (!IS_ERR_OR_NULL(hcd->phy)) {
                (void) otg_set_host(hcd->phy->otg, 0);
                usb_put_phy(hcd->phy);
@@ -418,76 +413,6 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
 
 /*-------------------------------------------------------------------------*/
 
-static int
-ohci_omap_start (struct usb_hcd *hcd)
-{
-       struct omap_usb_config *config;
-       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
-       int             ret;
-
-       if (!host_enabled)
-               return 0;
-       config = dev_get_platdata(hcd->self.controller);
-       if (config->otg || config->rwc) {
-               ohci->hc_control = OHCI_CTRL_RWC;
-               writel(OHCI_CTRL_RWC, &ohci->regs->control);
-       }
-
-       if ((ret = ohci_run (ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start\n");
-               ohci_stop (hcd);
-               return ret;
-       }
-       return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static const struct hc_driver ohci_omap_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "OMAP OHCI",
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset =                ohci_omap_init,
-       .start =                ohci_omap_start,
-       .stop =                 ohci_omap_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_hub_status_data,
-       .hub_control =          ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
-
 static int ohci_hcd_omap_drv_probe(struct platform_device *dev)
 {
        return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev);
@@ -506,16 +431,23 @@ static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
 
 #ifdef CONFIG_PM
 
-static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message)
+static int ohci_omap_suspend(struct platform_device *pdev, pm_message_t message)
 {
-       struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev));
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       bool do_wakeup = device_may_wakeup(&pdev->dev);
+       int ret;
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
 
+       ret = ohci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
+
        omap_ohci_clock_power(0);
-       return 0;
+       return ret;
 }
 
 static int ohci_omap_resume(struct platform_device *dev)
@@ -553,4 +485,29 @@ static struct platform_driver ohci_hcd_omap_driver = {
        },
 };
 
+static const struct ohci_driver_overrides omap_overrides __initconst = {
+       .product_desc   = "OMAP OHCI",
+       .reset          = ohci_omap_reset
+};
+
+static int __init ohci_omap_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_omap_hc_driver, &omap_overrides);
+       return platform_driver_register(&ohci_hcd_omap_driver);
+}
+module_init(ohci_omap_init);
+
+static void __exit ohci_omap_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_omap_driver);
+}
+module_exit(ohci_omap_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_ALIAS("platform:ohci");
+MODULE_LICENSE("GPL");
index a09af26f69ed4efdde274e028eb88110231cfc18..408d06a68571f423da0c4d41d0f0ad920bea44fe 100644 (file)
  *     - add kernel-doc
  */
 
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/usb/otg.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/dma-mapping.h>
-
-/*-------------------------------------------------------------------------*/
-
-static int ohci_omap3_init(struct usb_hcd *hcd)
-{
-       dev_dbg(hcd->self.controller, "starting OHCI controller\n");
-
-       return ohci_init(hcd_to_ohci(hcd));
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int ohci_omap3_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       /*
-        * RemoteWakeupConnected has to be set explicitly before
-        * calling ohci_run. The reset value of RWC is 0.
-        */
-       ohci->hc_control = OHCI_CTRL_RWC;
-       writel(OHCI_CTRL_RWC, &ohci->regs->control);
-
-       ret = ohci_run(ohci);
-
-       if (ret < 0) {
-               dev_err(hcd->self.controller, "can't start\n");
-               ohci_stop(hcd);
-       }
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
 
-       return ret;
-}
+#include "ohci.h"
 
-/*-------------------------------------------------------------------------*/
+#define DRIVER_DESC "OHCI OMAP3 driver"
 
-static const struct hc_driver ohci_omap3_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "OMAP3 OHCI Host Controller",
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset =                ohci_omap3_init,
-       .start =                ohci_omap3_start,
-       .stop =                 ohci_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_hub_status_data,
-       .hub_control =          ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
+static const char hcd_name[] = "ohci-omap3";
+static struct hc_driver __read_mostly ohci_omap3_hc_driver;
 
 /*
  * configure so an HC device and id are always provided
@@ -129,6 +61,7 @@ static const struct hc_driver ohci_omap3_hc_driver = {
 static int ohci_hcd_omap3_probe(struct platform_device *pdev)
 {
        struct device           *dev = &pdev->dev;
+       struct ohci_hcd         *ohci;
        struct usb_hcd          *hcd = NULL;
        void __iomem            *regs = NULL;
        struct resource         *res;
@@ -185,7 +118,12 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev)
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
 
-       ohci_hcd_init(hcd_to_ohci(hcd));
+       ohci = hcd_to_ohci(hcd);
+       /*
+        * RemoteWakeupConnected has to be set explicitly before
+        * calling ohci_run. The reset value of RWC is 0.
+        */
+       ohci->hc_control = OHCI_CTRL_RWC;
 
        ret = usb_add_hcd(hcd, irq, 0);
        if (ret) {
@@ -248,5 +186,25 @@ static struct platform_driver ohci_hcd_omap3_driver = {
        },
 };
 
+static int __init ohci_omap3_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_omap3_hc_driver, NULL);
+       return platform_driver_register(&ohci_hcd_omap3_driver);
+}
+module_init(ohci_omap3_init);
+
+static void __exit ohci_omap3_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_omap3_driver);
+}
+module_exit(ohci_omap3_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_ALIAS("platform:ohci-omap3");
 MODULE_AUTHOR("Anand Gadiyar <gadiyar@ti.com>");
+MODULE_LICENSE("GPL");
index ec337c2bd5e0b5294efa467691ef1807742aa1bc..90879e9ccbec302e8c5272d45e4847009b3730c4 100644 (file)
@@ -150,28 +150,16 @@ static int ohci_quirk_nec(struct usb_hcd *hcd)
 static int ohci_quirk_amd700(struct usb_hcd *hcd)
 {
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       struct pci_dev *amd_smbus_dev;
-       u8 rev;
 
        if (usb_amd_find_chipset_info())
                ohci->flags |= OHCI_QUIRK_AMD_PLL;
 
-       amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
-                       PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
-       if (!amd_smbus_dev)
-               return 0;
-
-       rev = amd_smbus_dev->revision;
-
        /* SB800 needs pre-fetch fix */
-       if ((rev >= 0x40) && (rev <= 0x4f)) {
+       if (usb_amd_prefetch_quirk()) {
                ohci->flags |= OHCI_QUIRK_AMD_PREFETCH;
                ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
        }
 
-       pci_dev_put(amd_smbus_dev);
-       amd_smbus_dev = NULL;
-
        return 0;
 }
 
@@ -323,3 +311,4 @@ module_exit(ohci_pci_cleanup);
 
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: ehci_pci");
index a4c6410f0ed494bfb6fb492849d1ed5f3b4fb1d2..f351ff5b171f752f121119cdc1e51d5846d4d6f0 100644 (file)
@@ -139,14 +139,21 @@ static int ohci_platform_remove(struct platform_device *dev)
 
 static int ohci_platform_suspend(struct device *dev)
 {
-       struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct usb_ohci_pdata *pdata = dev->platform_data;
        struct platform_device *pdev =
                container_of(dev, struct platform_device, dev);
+       bool do_wakeup = device_may_wakeup(dev);
+       int ret;
+
+       ret = ohci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
 
        if (pdata->power_suspend)
                pdata->power_suspend(pdev);
 
-       return 0;
+       return ret;
 }
 
 static int ohci_platform_resume(struct device *dev)
index 93371a235e821fac080068f7522bc5a80576119c..deea5d1d6394f3015de6a8a728061a0d3cfedc5a 100644 (file)
  * This file is licenced under the GPL.
  */
 
-#include <linux/device.h>
-#include <linux/signal.h>
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
-#include <mach/hardware.h>
 #include <linux/platform_data/usb-ohci-pxa27x.h>
 #include <linux/platform_data/usb-pxa3xx-ulpi.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
+
+#include <mach/hardware.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI PXA27x/PXA3x driver"
 
 /*
  * UHC: USB Host Controller (OHCI-like) register definitions
 
 #define PXA_UHC_MAX_PORTNUM    3
 
-struct pxa27x_ohci {
-       /* must be 1st member here for hcd_to_ohci() to work */
-       struct ohci_hcd ohci;
+static const char hcd_name[] = "ohci-pxa27x";
+
+static struct hc_driver __read_mostly ohci_pxa27x_hc_driver;
 
-       struct device   *dev;
+struct pxa27x_ohci {
        struct clk      *clk;
        void __iomem    *mmio_base;
 };
 
-#define to_pxa27x_ohci(hcd)    (struct pxa27x_ohci *)hcd_to_ohci(hcd)
+#define to_pxa27x_ohci(hcd)    (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv)
 
 /*
   PMM_NPS_MODE -- PMM Non-power switching mode
@@ -122,10 +133,10 @@ struct pxa27x_ohci {
   PMM_PERPORT_MODE -- PMM per port switching mode
       Ports are powered individually.
  */
-static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode)
+static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode)
 {
-       uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA);
-       uint32_t uhcrhdb = __raw_readl(ohci->mmio_base + UHCRHDB);
+       uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA);
+       uint32_t uhcrhdb = __raw_readl(pxa_ohci->mmio_base + UHCRHDB);
 
        switch (mode) {
        case PMM_NPS_MODE:
@@ -149,20 +160,18 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode)
                uhcrhda |= RH_A_NPS;
        }
 
-       __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA);
-       __raw_writel(uhcrhdb, ohci->mmio_base + UHCRHDB);
+       __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA);
+       __raw_writel(uhcrhdb, pxa_ohci->mmio_base + UHCRHDB);
        return 0;
 }
 
-extern int usb_disabled(void);
-
 /*-------------------------------------------------------------------------*/
 
-static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci,
+static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci,
                                   struct pxaohci_platform_data *inf)
 {
-       uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR);
-       uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA);
+       uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR);
+       uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA);
 
        if (inf->flags & ENABLE_PORT1)
                uhchr &= ~UHCHR_SSEP1;
@@ -194,17 +203,17 @@ static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci,
                uhcrhda |= UHCRHDA_POTPGT(inf->power_on_delay / 2);
        }
 
-       __raw_writel(uhchr, ohci->mmio_base + UHCHR);
-       __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA);
+       __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
+       __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA);
 }
 
-static inline void pxa27x_reset_hc(struct pxa27x_ohci *ohci)
+static inline void pxa27x_reset_hc(struct pxa27x_ohci *pxa_ohci)
 {
-       uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR);
+       uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR);
 
-       __raw_writel(uhchr | UHCHR_FHR, ohci->mmio_base + UHCHR);
+       __raw_writel(uhchr | UHCHR_FHR, pxa_ohci->mmio_base + UHCHR);
        udelay(11);
-       __raw_writel(uhchr & ~UHCHR_FHR, ohci->mmio_base + UHCHR);
+       __raw_writel(uhchr & ~UHCHR_FHR, pxa_ohci->mmio_base + UHCHR);
 }
 
 #ifdef CONFIG_PXA27x
@@ -213,25 +222,26 @@ extern void pxa27x_clear_otgph(void);
 #define pxa27x_clear_otgph()   do {} while (0)
 #endif
 
-static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev)
+static int pxa27x_start_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev)
 {
        int retval = 0;
        struct pxaohci_platform_data *inf;
        uint32_t uhchr;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
 
        inf = dev_get_platdata(dev);
 
-       clk_prepare_enable(ohci->clk);
+       clk_prepare_enable(pxa_ohci->clk);
 
-       pxa27x_reset_hc(ohci);
+       pxa27x_reset_hc(pxa_ohci);
 
-       uhchr = __raw_readl(ohci->mmio_base + UHCHR) | UHCHR_FSBIR;
-       __raw_writel(uhchr, ohci->mmio_base + UHCHR);
+       uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) | UHCHR_FSBIR;
+       __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
 
-       while (__raw_readl(ohci->mmio_base + UHCHR) & UHCHR_FSBIR)
+       while (__raw_readl(pxa_ohci->mmio_base + UHCHR) & UHCHR_FSBIR)
                cpu_relax();
 
-       pxa27x_setup_hc(ohci, inf);
+       pxa27x_setup_hc(pxa_ohci, inf);
 
        if (inf->init)
                retval = inf->init(dev);
@@ -240,38 +250,39 @@ static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev)
                return retval;
 
        if (cpu_is_pxa3xx())
-               pxa3xx_u2d_start_hc(&ohci_to_hcd(&ohci->ohci)->self);
+               pxa3xx_u2d_start_hc(&hcd->self);
 
-       uhchr = __raw_readl(ohci->mmio_base + UHCHR) & ~UHCHR_SSE;
-       __raw_writel(uhchr, ohci->mmio_base + UHCHR);
-       __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, ohci->mmio_base + UHCHIE);
+       uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) & ~UHCHR_SSE;
+       __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
+       __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, pxa_ohci->mmio_base + UHCHIE);
 
        /* Clear any OTG Pin Hold */
        pxa27x_clear_otgph();
        return 0;
 }
 
-static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev)
+static void pxa27x_stop_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev)
 {
        struct pxaohci_platform_data *inf;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
        uint32_t uhccoms;
 
        inf = dev_get_platdata(dev);
 
        if (cpu_is_pxa3xx())
-               pxa3xx_u2d_stop_hc(&ohci_to_hcd(&ohci->ohci)->self);
+               pxa3xx_u2d_stop_hc(&hcd->self);
 
        if (inf->exit)
                inf->exit(dev);
 
-       pxa27x_reset_hc(ohci);
+       pxa27x_reset_hc(pxa_ohci);
 
        /* Host Controller Reset */
-       uhccoms = __raw_readl(ohci->mmio_base + UHCCOMS) | 0x01;
-       __raw_writel(uhccoms, ohci->mmio_base + UHCCOMS);
+       uhccoms = __raw_readl(pxa_ohci->mmio_base + UHCCOMS) | 0x01;
+       __raw_writel(uhccoms, pxa_ohci->mmio_base + UHCCOMS);
        udelay(10);
 
-       clk_disable_unprepare(ohci->clk);
+       clk_disable_unprepare(pxa_ohci->clk);
 }
 
 #ifdef CONFIG_OF
@@ -356,7 +367,8 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
        int retval, irq;
        struct usb_hcd *hcd;
        struct pxaohci_platform_data *inf;
-       struct pxa27x_ohci *ohci;
+       struct pxa27x_ohci *pxa_ohci;
+       struct ohci_hcd *ohci;
        struct resource *r;
        struct clk *usb_clk;
 
@@ -409,29 +421,31 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
        }
 
        /* initialize "struct pxa27x_ohci" */
-       ohci = (struct pxa27x_ohci *)hcd_to_ohci(hcd);
-       ohci->dev = &pdev->dev;
-       ohci->clk = usb_clk;
-       ohci->mmio_base = (void __iomem *)hcd->regs;
+       pxa_ohci = to_pxa27x_ohci(hcd);
+       pxa_ohci->clk = usb_clk;
+       pxa_ohci->mmio_base = (void __iomem *)hcd->regs;
 
-       if ((retval = pxa27x_start_hc(ohci, &pdev->dev)) < 0) {
+       retval = pxa27x_start_hc(pxa_ohci, &pdev->dev);
+       if (retval < 0) {
                pr_debug("pxa27x_start_hc failed");
                goto err3;
        }
 
        /* Select Power Management Mode */
-       pxa27x_ohci_select_pmm(ohci, inf->port_mode);
+       pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode);
 
        if (inf->power_budget)
                hcd->power_budget = inf->power_budget;
 
-       ohci_hcd_init(hcd_to_ohci(hcd));
+       /* The value of NDP in roothub_a is incorrect on this hardware */
+       ohci = hcd_to_ohci(hcd);
+       ohci->num_ports = 3;
 
        retval = usb_add_hcd(hcd, irq, 0);
        if (retval == 0)
                return retval;
 
-       pxa27x_stop_hc(ohci, &pdev->dev);
+       pxa27x_stop_hc(pxa_ohci, &pdev->dev);
  err3:
        iounmap(hcd->regs);
  err2:
@@ -459,88 +473,18 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
  */
 void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)
 {
-       struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd);
+       struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
 
        usb_remove_hcd(hcd);
-       pxa27x_stop_hc(ohci, &pdev->dev);
+       pxa27x_stop_hc(pxa_ohci, &pdev->dev);
        iounmap(hcd->regs);
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+       clk_put(pxa_ohci->clk);
        usb_put_hcd(hcd);
-       clk_put(ohci->clk);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int
-ohci_pxa27x_start (struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
-       int             ret;
-
-       ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
-
-       /* The value of NDP in roothub_a is incorrect on this hardware */
-       ohci->num_ports = 3;
-
-       if ((ret = ohci_init(ohci)) < 0)
-               return ret;
-
-       if ((ret = ohci_run (ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start %s",
-                       hcd->self.bus_name);
-               ohci_stop (hcd);
-               return ret;
-       }
-
-       return 0;
 }
 
 /*-------------------------------------------------------------------------*/
 
-static const struct hc_driver ohci_pxa27x_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "PXA27x OHCI",
-       .hcd_priv_size =        sizeof(struct pxa27x_ohci),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .start =                ohci_pxa27x_start,
-       .stop =                 ohci_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_hub_status_data,
-       .hub_control =          ohci_hub_control,
-#ifdef  CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
-
 static int ohci_hcd_pxa27x_drv_probe(struct platform_device *pdev)
 {
        pr_debug ("In ohci_hcd_pxa27x_drv_probe");
@@ -563,32 +507,42 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev)
 static int ohci_hcd_pxa27x_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd);
+       struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       bool do_wakeup = device_may_wakeup(dev);
+       int ret;
+
 
-       if (time_before(jiffies, ohci->ohci.next_statechange))
+       if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
-       ohci->ohci.next_statechange = jiffies;
+       ohci->next_statechange = jiffies;
 
-       pxa27x_stop_hc(ohci, dev);
-       return 0;
+       ret = ohci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
+
+       pxa27x_stop_hc(pxa_ohci, dev);
+       return ret;
 }
 
 static int ohci_hcd_pxa27x_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd);
+       struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
        struct pxaohci_platform_data *inf = dev_get_platdata(dev);
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
        int status;
 
-       if (time_before(jiffies, ohci->ohci.next_statechange))
+       if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
-       ohci->ohci.next_statechange = jiffies;
+       ohci->next_statechange = jiffies;
 
-       if ((status = pxa27x_start_hc(ohci, dev)) < 0)
+       status = pxa27x_start_hc(pxa_ohci, dev);
+       if (status < 0)
                return status;
 
        /* Select Power Management Mode */
-       pxa27x_ohci_select_pmm(ohci, inf->port_mode);
+       pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode);
 
        ohci_resume(hcd, false);
        return 0;
@@ -600,9 +554,6 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
 };
 #endif
 
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:pxa27x-ohci");
-
 static struct platform_driver ohci_hcd_pxa27x_driver = {
        .probe          = ohci_hcd_pxa27x_drv_probe,
        .remove         = ohci_hcd_pxa27x_drv_remove,
@@ -617,3 +568,27 @@ static struct platform_driver ohci_hcd_pxa27x_driver = {
        },
 };
 
+static const struct ohci_driver_overrides pxa27x_overrides __initconst = {
+       .extra_priv_size =      sizeof(struct pxa27x_ohci),
+};
+
+static int __init ohci_pxa27x_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides);
+       return platform_driver_register(&ohci_hcd_pxa27x_driver);
+}
+module_init(ohci_pxa27x_init);
+
+static void __exit ohci_pxa27x_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_pxa27x_driver);
+}
+module_exit(ohci_pxa27x_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa27x-ohci");
index 4919afa4125e36eaeed297277da4631cb9cb5c7b..be3429e08d909a58b3b2d3fb9923ca592d4eabc4 100644 (file)
  * This file is licenced under the GPL.
 */
 
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/platform_data/usb-ohci-s3c2410.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
 
 #define valid_port(idx) ((idx) == 1 || (idx) == 2)
 
 /* clock device associated with the hcd */
 
+
+#define DRIVER_DESC "OHCI S3C2410 driver"
+
+static const char hcd_name[] = "ohci-s3c2410";
+
 static struct clk *clk;
 static struct clk *usb_clk;
 
 /* forward definitions */
 
+static int (*orig_ohci_hub_control)(struct usb_hcd  *hcd, u16 typeReq,
+                       u16 wValue, u16 wIndex, char *buf, u16 wLength);
+static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
+
 static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc);
 
 /* conversion functions */
@@ -93,7 +110,7 @@ ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf)
        int orig;
        int portno;
 
-       orig  = ohci_hub_status_data(hcd, buf);
+       orig = orig_ohci_hub_status_data(hcd, buf);
 
        if (info == NULL)
                return orig;
@@ -164,7 +181,7 @@ static int ohci_s3c2410_hub_control(
         * process the request straight away and exit */
 
        if (info == NULL) {
-               ret = ohci_hub_control(hcd, typeReq, wValue,
+               ret = orig_ohci_hub_control(hcd, typeReq, wValue,
                                       wIndex, buf, wLength);
                goto out;
        }
@@ -214,7 +231,7 @@ static int ohci_s3c2410_hub_control(
                break;
        }
 
-       ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+       ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
        if (ret)
                goto out;
 
@@ -374,8 +391,6 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
 
        s3c2410_start_hc(dev, hcd);
 
-       ohci_hcd_init(hcd_to_ohci(hcd));
-
        retval = usb_add_hcd(hcd, dev->resource[1].start, 0);
        if (retval != 0)
                goto err_ioremap;
@@ -392,71 +407,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
 
 /*-------------------------------------------------------------------------*/
 
-static int
-ohci_s3c2410_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       ret = ohci_init(ohci);
-       if (ret < 0)
-               return ret;
-
-       ret = ohci_run(ohci);
-       if (ret < 0) {
-               dev_err(hcd->self.controller, "can't start %s\n",
-                       hcd->self.bus_name);
-               ohci_stop(hcd);
-               return ret;
-       }
-
-       return 0;
-}
-
-
-static const struct hc_driver ohci_s3c2410_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "S3C24XX OHCI",
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .start =                ohci_s3c2410_start,
-       .stop =                 ohci_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_s3c2410_hub_status_data,
-       .hub_control =          ohci_s3c2410_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/* device driver */
+static struct hc_driver __read_mostly ohci_s3c2410_hc_driver;
 
 static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev)
 {
@@ -533,4 +484,39 @@ static struct platform_driver ohci_hcd_s3c2410_driver = {
        },
 };
 
+static int __init ohci_s3c2410_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ohci_init_driver(&ohci_s3c2410_hc_driver, NULL);
+
+       /*
+        * The Samsung HW has some unusual quirks, which require
+        * Sumsung-specific workarounds. We override certain hc_driver
+        * functions here to achieve that. We explicitly do not enhance
+        * ohci_driver_overrides to allow this more easily, since this
+        * is an unusual case, and we don't want to encourage others to
+        * override these functions by making it too easy.
+        */
+
+       orig_ohci_hub_control = ohci_s3c2410_hc_driver.hub_control;
+       orig_ohci_hub_status_data = ohci_s3c2410_hc_driver.hub_status_data;
+
+       ohci_s3c2410_hc_driver.hub_status_data  = ohci_s3c2410_hub_status_data;
+       ohci_s3c2410_hc_driver.hub_control      = ohci_s3c2410_hub_control;
+
+       return platform_driver_register(&ohci_hcd_s3c2410_driver);
+}
+module_init(ohci_s3c2410_init);
+
+static void __exit ohci_s3c2410_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_s3c2410_driver);
+}
+module_exit(ohci_s3c2410_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:s3c2410-ohci");
index d479d5ddab8853b9844f29ffa7e15b1ca0beaca7..2a5de5fecd8f5ba3a05a449485dd3f7fae7b39b2 100644 (file)
@@ -216,14 +216,21 @@ static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev)
 static int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg)
 {
        struct device *dev = &pdev->dev;
-       struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(pdev));
+       struct usb_hcd  *hcd = platform_get_drvdata(pdev);
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       bool do_wakeup = device_may_wakeup(dev);
+       int ret;
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
 
+       ret = ohci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
+
        sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 0);
-       return 0;
+       return ret;
 }
 
 static int ohci_sm501_resume(struct platform_device *pdev)
index cc9dd9e4f05e69469eeca87233dd0f29bac23992..31ff3fc4e26fd19a66bc26803a9112759a0f2ab7 100644 (file)
 * warranty of any kind, whether express or implied.
 */
 
-#include <linux/signal.h>
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
 
+#define DRIVER_DESC "OHCI SPEAr driver"
+
+static const char hcd_name[] = "SPEAr-ohci";
 struct spear_ohci {
-       struct ohci_hcd ohci;
        struct clk *clk;
 };
 
-#define to_spear_ohci(hcd)     (struct spear_ohci *)hcd_to_ohci(hcd)
-
-static void spear_start_ohci(struct spear_ohci *ohci)
-{
-       clk_prepare_enable(ohci->clk);
-}
-
-static void spear_stop_ohci(struct spear_ohci *ohci)
-{
-       clk_disable_unprepare(ohci->clk);
-}
-
-static int ohci_spear_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       ret = ohci_init(ohci);
-       if (ret < 0)
-               return ret;
-       ohci->regs = hcd->regs;
-
-       ret = ohci_run(ohci);
-       if (ret < 0) {
-               dev_err(hcd->self.controller, "can't start\n");
-               ohci_stop(hcd);
-               return ret;
-       }
-
-       create_debug_files(ohci);
-
-#ifdef DEBUG
-       ohci_dump(ohci, 1);
-#endif
-       return 0;
-}
-
-static const struct hc_driver ohci_spear_hc_driver = {
-       .description            = hcd_name,
-       .product_desc           = "SPEAr OHCI",
-       .hcd_priv_size          = sizeof(struct spear_ohci),
-
-       /* generic hardware linkage */
-       .irq                    = ohci_irq,
-       .flags                  = HCD_USB11 | HCD_MEMORY,
-
-       /* basic lifecycle operations */
-       .start                  = ohci_spear_start,
-       .stop                   = ohci_stop,
-       .shutdown               = ohci_shutdown,
-#ifdef CONFIG_PM
-       .bus_suspend            = ohci_bus_suspend,
-       .bus_resume             = ohci_bus_resume,
-#endif
-
-       /* managing i/o requests and associated device resources */
-       .urb_enqueue            = ohci_urb_enqueue,
-       .urb_dequeue            = ohci_urb_dequeue,
-       .endpoint_disable       = ohci_endpoint_disable,
-
-       /* scheduling support */
-       .get_frame_number       = ohci_get_frame,
+#define to_spear_ohci(hcd)     (struct spear_ohci *)(hcd_to_ohci(hcd)->priv)
 
-       /* root hub support */
-       .hub_status_data        = ohci_hub_status_data,
-       .hub_control            = ohci_hub_control,
-
-       .start_port_reset       = ohci_start_port_reset,
-};
+static struct hc_driver __read_mostly ohci_spear_hc_driver;
 
 static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
 {
        const struct hc_driver *driver = &ohci_spear_hc_driver;
+       struct ohci_hcd *ohci;
        struct usb_hcd *hcd = NULL;
        struct clk *usbh_clk;
-       struct spear_ohci *ohci_p;
+       struct spear_ohci *sohci_p;
        struct resource *res;
        int retval, irq;
 
@@ -151,16 +96,18 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
                goto err_put_hcd;
        }
 
-       ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd);
-       ohci_p->clk = usbh_clk;
-       spear_start_ohci(ohci_p);
-       ohci_hcd_init(hcd_to_ohci(hcd));
+       sohci_p = to_spear_ohci(hcd);
+       sohci_p->clk = usbh_clk;
+
+       clk_prepare_enable(sohci_p->clk);
+
+       ohci = hcd_to_ohci(hcd);
 
        retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0);
        if (retval == 0)
                return retval;
 
-       spear_stop_ohci(ohci_p);
+       clk_disable_unprepare(sohci_p->clk);
 err_put_hcd:
        usb_put_hcd(hcd);
 fail:
@@ -172,11 +119,11 @@ fail:
 static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
-       struct spear_ohci *ohci_p = to_spear_ohci(hcd);
+       struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 
        usb_remove_hcd(hcd);
-       if (ohci_p->clk)
-               spear_stop_ohci(ohci_p);
+       if (sohci_p->clk)
+               clk_disable_unprepare(sohci_p->clk);
 
        usb_put_hcd(hcd);
        return 0;
@@ -188,13 +135,14 @@ static int spear_ohci_hcd_drv_suspend(struct platform_device *dev,
 {
        struct usb_hcd *hcd = platform_get_drvdata(dev);
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       struct spear_ohci *ohci_p = to_spear_ohci(hcd);
+       struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
 
-       spear_stop_ohci(ohci_p);
+       clk_disable_unprepare(sohci_p->clk);
+
        return 0;
 }
 
@@ -202,13 +150,13 @@ static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(dev);
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       struct spear_ohci *ohci_p = to_spear_ohci(hcd);
+       struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
 
-       spear_start_ohci(ohci_p);
+       clk_prepare_enable(sohci_p->clk);
        ohci_resume(hcd, false);
        return 0;
 }
@@ -234,4 +182,28 @@ static struct platform_driver spear_ohci_hcd_driver = {
        },
 };
 
+static const struct ohci_driver_overrides spear_overrides __initconst = {
+       .extra_priv_size = sizeof(struct spear_ohci),
+};
+static int __init ohci_spear_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_spear_hc_driver, &spear_overrides);
+       return platform_driver_register(&spear_ohci_hcd_driver);
+}
+module_init(ohci_spear_init);
+
+static void __exit ohci_spear_cleanup(void)
+{
+       platform_driver_unregister(&spear_ohci_hcd_driver);
+}
+module_exit(ohci_spear_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Deepak Sikri");
+MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:spear-ohci");
index 08ef2829a7e2b1c06bbd0af51f308884c8ad9a26..dfbdd3aefe98a8079fd6faf3f5713afd74f97fc6 100644 (file)
 #define USB_INTEL_USB3_PSSEN   0xD8
 #define USB_INTEL_USB3PRM      0xDC
 
+/*
+ * amd_chipset_gen values represent AMD different chipset generations
+ */
+enum amd_chipset_gen {
+       NOT_AMD_CHIPSET = 0,
+       AMD_CHIPSET_SB600,
+       AMD_CHIPSET_SB700,
+       AMD_CHIPSET_SB800,
+       AMD_CHIPSET_HUDSON2,
+       AMD_CHIPSET_BOLTON,
+       AMD_CHIPSET_YANGTZE,
+       AMD_CHIPSET_UNKNOWN,
+};
+
+struct amd_chipset_type {
+       enum amd_chipset_gen gen;
+       u8 rev;
+};
+
 static struct amd_chipset_info {
        struct pci_dev  *nb_dev;
        struct pci_dev  *smbus_dev;
        int nb_type;
-       int sb_type;
+       struct amd_chipset_type sb_type;
        int isoc_reqs;
        int probe_count;
        int probe_result;
@@ -91,6 +110,51 @@ static struct amd_chipset_info {
 
 static DEFINE_SPINLOCK(amd_lock);
 
+/*
+ * amd_chipset_sb_type_init - initialize amd chipset southbridge type
+ *
+ * AMD FCH/SB generation and revision is identified by SMBus controller
+ * vendor, device and revision IDs.
+ *
+ * Returns: 1 if it is an AMD chipset, 0 otherwise.
+ */
+static int amd_chipset_sb_type_init(struct amd_chipset_info *pinfo)
+{
+       u8 rev = 0;
+       pinfo->sb_type.gen = AMD_CHIPSET_UNKNOWN;
+
+       pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+                       PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
+       if (pinfo->smbus_dev) {
+               rev = pinfo->smbus_dev->revision;
+               if (rev >= 0x10 && rev <= 0x1f)
+                       pinfo->sb_type.gen = AMD_CHIPSET_SB600;
+               else if (rev >= 0x30 && rev <= 0x3f)
+                       pinfo->sb_type.gen = AMD_CHIPSET_SB700;
+               else if (rev >= 0x40 && rev <= 0x4f)
+                       pinfo->sb_type.gen = AMD_CHIPSET_SB800;
+       } else {
+               pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+                               PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
+
+               if (!pinfo->smbus_dev) {
+                       pinfo->sb_type.gen = NOT_AMD_CHIPSET;
+                       return 0;
+               }
+
+               rev = pinfo->smbus_dev->revision;
+               if (rev >= 0x11 && rev <= 0x14)
+                       pinfo->sb_type.gen = AMD_CHIPSET_HUDSON2;
+               else if (rev >= 0x15 && rev <= 0x18)
+                       pinfo->sb_type.gen = AMD_CHIPSET_BOLTON;
+               else if (rev >= 0x39 && rev <= 0x3a)
+                       pinfo->sb_type.gen = AMD_CHIPSET_YANGTZE;
+       }
+
+       pinfo->sb_type.rev = rev;
+       return 1;
+}
+
 void sb800_prefetch(struct device *dev, int on)
 {
        u16 misc;
@@ -106,7 +170,6 @@ EXPORT_SYMBOL_GPL(sb800_prefetch);
 
 int usb_amd_find_chipset_info(void)
 {
-       u8 rev = 0;
        unsigned long flags;
        struct amd_chipset_info info;
        int ret;
@@ -122,27 +185,17 @@ int usb_amd_find_chipset_info(void)
        memset(&info, 0, sizeof(info));
        spin_unlock_irqrestore(&amd_lock, flags);
 
-       info.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
-       if (info.smbus_dev) {
-               rev = info.smbus_dev->revision;
-               if (rev >= 0x40)
-                       info.sb_type = 1;
-               else if (rev >= 0x30 && rev <= 0x3b)
-                       info.sb_type = 3;
-       } else {
-               info.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
-                                               0x780b, NULL);
-               if (!info.smbus_dev) {
-                       ret = 0;
-                       goto commit;
-               }
-
-               rev = info.smbus_dev->revision;
-               if (rev >= 0x11 && rev <= 0x18)
-                       info.sb_type = 2;
+       if (!amd_chipset_sb_type_init(&info)) {
+               ret = 0;
+               goto commit;
        }
 
-       if (info.sb_type == 0) {
+       /* Below chipset generations needn't enable AMD PLL quirk */
+       if (info.sb_type.gen == AMD_CHIPSET_UNKNOWN ||
+                       info.sb_type.gen == AMD_CHIPSET_SB600 ||
+                       info.sb_type.gen == AMD_CHIPSET_YANGTZE ||
+                       (info.sb_type.gen == AMD_CHIPSET_SB700 &&
+                       info.sb_type.rev > 0x3b)) {
                if (info.smbus_dev) {
                        pci_dev_put(info.smbus_dev);
                        info.smbus_dev = NULL;
@@ -197,6 +250,39 @@ commit:
 }
 EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);
 
+int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev)
+{
+       /* Make sure amd chipset type has already been initialized */
+       usb_amd_find_chipset_info();
+       if (amd_chipset.sb_type.gen != AMD_CHIPSET_YANGTZE)
+               return 0;
+
+       dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n");
+       return 1;
+}
+EXPORT_SYMBOL_GPL(usb_hcd_amd_remote_wakeup_quirk);
+
+bool usb_amd_hang_symptom_quirk(void)
+{
+       u8 rev;
+
+       usb_amd_find_chipset_info();
+       rev = amd_chipset.sb_type.rev;
+       /* SB600 and old version of SB700 have hang symptom bug */
+       return amd_chipset.sb_type.gen == AMD_CHIPSET_SB600 ||
+                       (amd_chipset.sb_type.gen == AMD_CHIPSET_SB700 &&
+                        rev >= 0x3a && rev <= 0x3b);
+}
+EXPORT_SYMBOL_GPL(usb_amd_hang_symptom_quirk);
+
+bool usb_amd_prefetch_quirk(void)
+{
+       usb_amd_find_chipset_info();
+       /* SB800 needs pre-fetch fix */
+       return amd_chipset.sb_type.gen == AMD_CHIPSET_SB800;
+}
+EXPORT_SYMBOL_GPL(usb_amd_prefetch_quirk);
+
 /*
  * The hardware normally enables the A-link power management feature, which
  * lets the system lower the power consumption in idle states.
@@ -229,7 +315,9 @@ static void usb_amd_quirk_pll(int disable)
                }
        }
 
-       if (amd_chipset.sb_type == 1 || amd_chipset.sb_type == 2) {
+       if (amd_chipset.sb_type.gen == AMD_CHIPSET_SB800 ||
+                       amd_chipset.sb_type.gen == AMD_CHIPSET_HUDSON2 ||
+                       amd_chipset.sb_type.gen == AMD_CHIPSET_BOLTON) {
                outb_p(AB_REG_BAR_LOW, 0xcd6);
                addr_low = inb_p(0xcd7);
                outb_p(AB_REG_BAR_HIGH, 0xcd6);
@@ -240,7 +328,8 @@ static void usb_amd_quirk_pll(int disable)
                outl_p(0x40, AB_DATA(addr));
                outl_p(0x34, AB_INDX(addr));
                val = inl_p(AB_DATA(addr));
-       } else if (amd_chipset.sb_type == 3) {
+       } else if (amd_chipset.sb_type.gen == AMD_CHIPSET_SB700 &&
+                       amd_chipset.sb_type.rev <= 0x3b) {
                pci_read_config_dword(amd_chipset.smbus_dev,
                                        AB_REG_BAR_SB700, &addr);
                outl(AX_INDXC, AB_INDX(addr));
@@ -353,7 +442,7 @@ void usb_amd_dev_put(void)
        amd_chipset.nb_dev = NULL;
        amd_chipset.smbus_dev = NULL;
        amd_chipset.nb_type = 0;
-       amd_chipset.sb_type = 0;
+       memset(&amd_chipset.sb_type, 0, sizeof(amd_chipset.sb_type));
        amd_chipset.isoc_reqs = 0;
        amd_chipset.probe_result = 0;
 
index ed6700d00fe6fd372f62b575b49075b5ede392f1..638e88f7a28bbce2a3e04df49cee7dedde99229d 100644 (file)
@@ -5,6 +5,8 @@
 void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
 int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
 int usb_amd_find_chipset_info(void);
+bool usb_amd_hang_symptom_quirk(void);
+bool usb_amd_prefetch_quirk(void);
 void usb_amd_dev_put(void);
 void usb_amd_quirk_pll_disable(void);
 void usb_amd_quirk_pll_enable(void);
index 5477bf5df2186549412612dc016e809fa6970124..79620c39217ec182474bf53e2e04386560ea19b7 100644 (file)
@@ -1413,7 +1413,7 @@ static int sl811h_show(struct seq_file *s, void *unused)
                        case SL11H_CTL1MASK_SE0: s = " se0/reset"; break;
                        case SL11H_CTL1MASK_K: s = " k/resume"; break;
                        default: s = "j"; break;
-                       }; s; }),
+                       } s; }),
                        (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "",
                        (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : "");
 
@@ -1446,7 +1446,7 @@ static int sl811h_show(struct seq_file *s, void *unused)
                        case USB_PID_SETUP: s = "setup"; break;
                        case USB_PID_ACK: s = "status"; break;
                        default: s = "?"; break;
-                       }; s;}),
+                       } s;}),
                        ep->maxpacket,
                        ep->nak_count, ep->error_count);
                list_for_each_entry (urb, &ep->hep->urb_list, urb_list) {
index 45573754652534b94c28cc6fa8c85d1a35ac1385..8e239cdd95d51025c8ca806da08899a420a19e09 100644 (file)
@@ -310,14 +310,14 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
        unsigned short portsc1, portsc2;
 
 
-       usbcmd    = uhci_readw(uhci, 0);
-       usbstat   = uhci_readw(uhci, 2);
-       usbint    = uhci_readw(uhci, 4);
-       usbfrnum  = uhci_readw(uhci, 6);
-       flbaseadd = uhci_readl(uhci, 8);
-       sof       = uhci_readb(uhci, 12);
-       portsc1   = uhci_readw(uhci, 16);
-       portsc2   = uhci_readw(uhci, 18);
+       usbcmd    = uhci_readw(uhci, USBCMD);
+       usbstat   = uhci_readw(uhci, USBSTS);
+       usbint    = uhci_readw(uhci, USBINTR);
+       usbfrnum  = uhci_readw(uhci, USBFRNUM);
+       flbaseadd = uhci_readl(uhci, USBFLBASEADD);
+       sof       = uhci_readb(uhci, USBSOF);
+       portsc1   = uhci_readw(uhci, USBPORTSC1);
+       portsc2   = uhci_readw(uhci, USBPORTSC2);
 
        out += sprintf(out, "  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
                usbcmd,
index 9189bc984c98ce9804d51ec94c4aebe4147b8f3f..93e17b12fb3304b0191f36b3eb2f2b6eef334132 100644 (file)
@@ -75,8 +75,6 @@ static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
        return !!*buf;
 }
 
-#define OK(x)                  len = (x); break
-
 #define CLR_RH_PORTSTAT(x) \
        status = uhci_readw(uhci, port_addr);   \
        status &= ~(RWC_BITS|WZ_BITS); \
@@ -244,7 +242,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        u16 wIndex, char *buf, u16 wLength)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-       int status, lstatus, retval = 0, len = 0;
+       int status, lstatus, retval = 0;
        unsigned int port = wIndex - 1;
        unsigned long port_addr = USBPORTSC1 + 2 * port;
        u16 wPortChange, wPortStatus;
@@ -258,7 +256,8 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
        case GetHubStatus:
                *(__le32 *)buf = cpu_to_le32(0);
-               OK(4);          /* hub power */
+               retval = 4; /* hub power */
+               break;
        case GetPortStatus:
                if (port >= uhci->rh_numports)
                        goto err;
@@ -311,13 +310,14 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
                *(__le16 *)buf = cpu_to_le16(wPortStatus);
                *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
-               OK(4);
+               retval = 4;
+               break;
        case SetHubFeature:             /* We don't implement these */
        case ClearHubFeature:
                switch (wValue) {
                case C_HUB_OVER_CURRENT:
                case C_HUB_LOCAL_POWER:
-                       OK(0);
+                       break;
                default:
                        goto err;
                }
@@ -329,7 +329,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
                        SET_RH_PORTSTAT(USBPORTSC_SUSP);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_RESET:
                        SET_RH_PORTSTAT(USBPORTSC_PR);
 
@@ -338,10 +338,10 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
                        /* USB v2.0 7.1.7.5 */
                        uhci->ports_timeout = jiffies + msecs_to_jiffies(50);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_POWER:
                        /* UHCI has no power switching */
-                       OK(0);
+                       break;
                default:
                        goto err;
                }
@@ -356,10 +356,10 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
                        /* Disable terminates Resume signalling */
                        uhci_finish_suspend(uhci, port, port_addr);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_C_ENABLE:
                        CLR_RH_PORTSTAT(USBPORTSC_PEC);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_SUSPEND:
                        if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) {
 
@@ -382,32 +382,32 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                                        uhci->ports_timeout = jiffies +
                                                msecs_to_jiffies(20);
                        }
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_C_SUSPEND:
                        clear_bit(port, &uhci->port_c_suspend);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_POWER:
                        /* UHCI has no power switching */
                        goto err;
                case USB_PORT_FEAT_C_CONNECTION:
                        CLR_RH_PORTSTAT(USBPORTSC_CSC);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_C_OVER_CURRENT:
                        CLR_RH_PORTSTAT(USBPORTSC_OCC);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_C_RESET:
                        /* this driver won't report these */
-                       OK(0);
+                       break;
                default:
                        goto err;
                }
                break;
        case GetHubDescriptor:
-               len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
-               memcpy(buf, root_hub_hub_des, len);
-               if (len > 2)
+               retval = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
+               memcpy(buf, root_hub_hub_des, retval);
+               if (retval > 2)
                        buf[2] = uhci->rh_numports;
-               OK(len);
+               break;
        default:
 err:
                retval = -EPIPE;
index 0f228c46eedaab97c9351f784b8604d6144bb517..4cd79888804bb679b1ff85f850bb6d2e362d8fd3 100644 (file)
@@ -162,6 +162,8 @@ static void uhci_shutdown(struct pci_dev *pdev)
 
 #ifdef CONFIG_PM
 
+static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated);
+
 static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
@@ -174,12 +176,6 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
        if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
                goto done_okay;         /* Already suspended or dead */
 
-       if (uhci->rh_state > UHCI_RH_SUSPENDED) {
-               dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n");
-               rc = -EBUSY;
-               goto done;
-       };
-
        /* All PCI host controllers are required to disable IRQ generation
         * at the source, so we must turn off PIRQ.
         */
@@ -195,8 +191,15 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 
 done_okay:
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-done:
        spin_unlock_irq(&uhci->lock);
+
+       synchronize_irq(hcd->irq);
+
+       /* Check for race with a wakeup request */
+       if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+               uhci_pci_resume(hcd, false);
+               rc = -EBUSY;
+       }
        return rc;
 }
 
@@ -299,3 +302,5 @@ static struct pci_driver uhci_pci_driver = {
        },
 #endif
 };
+
+MODULE_SOFTDEP("pre: ehci_pci");
index d033a0ec7f0d02bd3874ae5e7339d43e964d2611..ded842bc65787a9ef74da721fbbfb7bac8fb479c 100644 (file)
@@ -105,8 +105,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
 
        uhci->regs = hcd->regs;
 
-       ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED |
-                                                               IRQF_SHARED);
+       ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
        if (ret)
                goto err_uhci;
 
index ecc88db804e008ba4b7eacb48f68aa1c6a49ded4..1b0888f8da9a156a4e0ae29597568efe008ddc94 100644 (file)
@@ -134,7 +134,7 @@ static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
        default:
                ret = asl_urb_enqueue(whc, urb, mem_flags);
                break;
-       };
+       }
 
        return ret;
 }
@@ -160,7 +160,7 @@ static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status)
        default:
                ret = asl_urb_dequeue(whc, urb, status);
                break;
-       };
+       }
 
        return ret;
 }
index e8b4c56dcf62adf1f5326e8609087cd387fedc2e..805f2348eeba09a3ced3e037a3af82592d7b7c30 100644 (file)
@@ -296,7 +296,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
        /* Wait for last stop endpoint command to finish */
        timeleft = wait_for_completion_interruptible_timeout(
                        cmd->completion,
-                       USB_CTRL_SET_TIMEOUT);
+                       XHCI_CMD_DEFAULT_TIMEOUT);
        if (timeleft <= 0) {
                xhci_warn(xhci, "%s while waiting for stop endpoint command\n",
                                timeleft == 0 ? "Timeout" : "Signal");
@@ -524,7 +524,8 @@ static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg)
  * the compliance mode timer is deleted. A port won't enter
  * compliance mode if it has previously entered U0.
  */
-void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
+static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
+                                   u16 wIndex)
 {
        u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1);
        bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
index 83bcd13622c3466e655a00166bf6e8076ae198bc..49b8bd063fab70ab2de943f77c58ac329cdad87f 100644 (file)
@@ -1693,9 +1693,7 @@ void xhci_free_command(struct xhci_hcd *xhci,
 void xhci_mem_cleanup(struct xhci_hcd *xhci)
 {
        struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
-       struct dev_info *dev_info, *next;
        struct xhci_cd  *cur_cd, *next_cd;
-       unsigned long   flags;
        int size;
        int i, j, num_ports;
 
@@ -1756,13 +1754,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 
        scratchpad_free(xhci);
 
-       spin_lock_irqsave(&xhci->lock, flags);
-       list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) {
-               list_del(&dev_info->list);
-               kfree(dev_info);
-       }
-       spin_unlock_irqrestore(&xhci->lock, flags);
-
        if (!xhci->rh_bw)
                goto no_bw;
 
@@ -2231,7 +2222,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
        u32 page_size, temp;
        int i;
 
-       INIT_LIST_HEAD(&xhci->lpm_failed_devs);
        INIT_LIST_HEAD(&xhci->cancel_cmd_list);
 
        page_size = xhci_readl(xhci, &xhci->op_regs->page_size);
index 6bfbd80ec2b9edfa0a079767381d7d733d8c7035..1e2f3f4958436fb120b81d41632ad7e7c76fb5b5 100644 (file)
@@ -178,7 +178,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
                        if (ring->type == TYPE_EVENT &&
                                        last_trb_on_last_seg(xhci, ring,
                                                ring->deq_seg, ring->dequeue)) {
-                               ring->cycle_state = (ring->cycle_state ? 0 : 1);
+                               ring->cycle_state ^= 1;
                        }
                        ring->deq_seg = ring->deq_seg->next;
                        ring->dequeue = ring->deq_seg->trbs;
@@ -726,7 +726,7 @@ static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
 
 /* Must be called with xhci->lock held in interrupt context */
 static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
-               struct xhci_td *cur_td, int status, char *adjective)
+               struct xhci_td *cur_td, int status)
 {
        struct usb_hcd *hcd;
        struct urb      *urb;
@@ -765,10 +765,9 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
  *  2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain
  *     bit cleared) so that the HW will skip over them.
  */
-static void handle_stopped_endpoint(struct xhci_hcd *xhci,
+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 slot_id;
        unsigned int ep_index;
        struct xhci_virt_device *virt_dev;
        struct xhci_ring *ep_ring;
@@ -779,10 +778,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
 
        struct xhci_dequeue_state deq_state;
 
-       if (unlikely(TRB_TO_SUSPEND_PORT(
-                            le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])))) {
-               slot_id = TRB_TO_SLOT_ID(
-                       le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]));
+       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,
@@ -795,7 +791,6 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
        }
 
        memset(&deq_state, 0, sizeof(deq_state));
-       slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
        ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
        ep = &xhci->devs[slot_id]->eps[ep_index];
 
@@ -891,7 +886,7 @@ remove_finished_td:
                /* Doesn't matter what we pass for status, since the core will
                 * just overwrite it (because the URB has been unlinked).
                 */
-               xhci_giveback_urb_in_irq(xhci, cur_td, 0, "cancelled");
+               xhci_giveback_urb_in_irq(xhci, cur_td, 0);
 
                /* Stop processing the cancelled list if the watchdog timer is
                 * running.
@@ -1001,7 +996,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
                                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, "killed");
+                                               -ESHUTDOWN);
                        }
                        while (!list_empty(&temp_ep->cancelled_td_list)) {
                                cur_td = list_first_entry(
@@ -1010,7 +1005,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
                                                cancelled_td_list);
                                list_del_init(&cur_td->cancelled_td_list);
                                xhci_giveback_urb_in_irq(xhci, cur_td,
-                                               -ESHUTDOWN, "killed");
+                                               -ESHUTDOWN);
                        }
                }
        }
@@ -1077,11 +1072,9 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
  * endpoint doorbell to restart the ring, but only if there aren't more
  * cancellations pending.
  */
-static void handle_set_deq_completion(struct xhci_hcd *xhci,
-               struct xhci_event_cmd *event,
-               union xhci_trb *trb)
+static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
+               union xhci_trb *trb, u32 cmd_comp_code)
 {
-       unsigned int slot_id;
        unsigned int ep_index;
        unsigned int stream_id;
        struct xhci_ring *ep_ring;
@@ -1089,7 +1082,6 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
        struct xhci_ep_ctx *ep_ctx;
        struct xhci_slot_ctx *slot_ctx;
 
-       slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
        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];
@@ -1107,11 +1099,11 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
        ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
        slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
 
-       if (GET_COMP_CODE(le32_to_cpu(event->status)) != COMP_SUCCESS) {
+       if (cmd_comp_code != COMP_SUCCESS) {
                unsigned int ep_state;
                unsigned int slot_state;
 
-               switch (GET_COMP_CODE(le32_to_cpu(event->status))) {
+               switch (cmd_comp_code) {
                case COMP_TRB_ERR:
                        xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because "
                                        "of stream ID configuration\n");
@@ -1134,7 +1126,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
                default:
                        xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown "
                                        "completion code of %u.\n",
-                                 GET_COMP_CODE(le32_to_cpu(event->status)));
+                                 cmd_comp_code);
                        break;
                }
                /* OK what do we do now?  The endpoint state is hosed, and we
@@ -1171,21 +1163,17 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
        ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
 }
 
-static void handle_reset_ep_completion(struct xhci_hcd *xhci,
-               struct xhci_event_cmd *event,
-               union xhci_trb *trb)
+static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
+               union xhci_trb *trb, u32 cmd_comp_code)
 {
-       int slot_id;
        unsigned int ep_index;
 
-       slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
        ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
        /* This command will only fail if the endpoint wasn't halted,
         * but we don't care.
         */
        xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
-               "Ignoring reset ep completion code of %u",
-                GET_COMP_CODE(le32_to_cpu(event->status)));
+               "Ignoring reset ep completion code of %u", cmd_comp_code);
 
        /* HW with the reset endpoint quirk needs to have a configure endpoint
         * command complete before the endpoint can be used.  Queue that here
@@ -1386,21 +1374,149 @@ static int handle_stopped_cmd_ring(struct xhci_hcd *xhci,
        return cur_trb_is_good;
 }
 
+static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
+               u32 cmd_comp_code)
+{
+       if (cmd_comp_code == COMP_SUCCESS)
+               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)
+{
+       struct xhci_virt_device *virt_dev;
+
+       virt_dev = xhci->devs[slot_id];
+       if (!virt_dev)
+               return;
+       if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
+               /* Delete default control endpoint resources */
+               xhci_free_device_endpoint_resources(xhci, virt_dev, true);
+       xhci_free_virt_device(xhci, slot_id);
+}
+
+static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
+               struct xhci_event_cmd *event, u32 cmd_comp_code)
+{
+       struct xhci_virt_device *virt_dev;
+       struct xhci_input_control_ctx *ctrl_ctx;
+       unsigned int ep_index;
+       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
+        * needed an extra configure endpoint command after a reset
+        * endpoint command or streams were being configured.
+        * If the command was for a halted endpoint, the xHCI driver
+        * is not waiting on the configure endpoint command.
+        */
+       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");
+               return;
+       }
+
+       add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+       drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
+       /* Input ctx add_flags are the endpoint index plus one */
+       ep_index = xhci_last_valid_endpoint(add_flags) - 1;
+
+       /* A usb_set_interface() call directly after clearing a halted
+        * condition may race on this quirky hardware.  Not worth
+        * worrying about, since this is prototype hardware.  Not sure
+        * if this will work for streams, but streams support was
+        * untested on this prototype.
+        */
+       if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
+                       ep_index != (unsigned int) -1 &&
+                       add_flags - SLOT_FLAG == drop_flags) {
+               ep_state = virt_dev->eps[ep_index].ep_state;
+               if (!(ep_state & EP_HALTED))
+                       goto bandwidth_change;
+               xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+                               "Completed config ep cmd - "
+                               "last ep index = %d, state = %d",
+                               ep_index, ep_state);
+               /* Clear internal halted state and restart ring(s) */
+               virt_dev->eps[ep_index].ep_state &= ~EP_HALTED;
+               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
+               xhci_warn(xhci, "Reset device command completion "
+                               "for disabled slot %u\n", slot_id);
+}
+
+static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
+               struct xhci_event_cmd *event)
+{
+       if (!(xhci->quirks & XHCI_NEC_HOST)) {
+               xhci->error_bitmask |= 1 << 6;
+               return;
+       }
+       xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+                       "NEC firmware version %2x.%02x",
+                       NEC_FW_MAJOR(le32_to_cpu(event->status)),
+                       NEC_FW_MINOR(le32_to_cpu(event->status)));
+}
+
 static void handle_cmd_completion(struct xhci_hcd *xhci,
                struct xhci_event_cmd *event)
 {
        int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
        u64 cmd_dma;
        dma_addr_t cmd_dequeue_dma;
-       struct xhci_input_control_ctx *ctrl_ctx;
-       struct xhci_virt_device *virt_dev;
-       unsigned int ep_index;
-       struct xhci_ring *ep_ring;
-       unsigned int ep_state;
+       u32 cmd_comp_code;
+       union xhci_trb *cmd_trb;
+       u32 cmd_type;
 
        cmd_dma = le64_to_cpu(event->cmd_trb);
+       cmd_trb = xhci->cmd_ring->dequeue;
        cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
-                       xhci->cmd_ring->dequeue);
+                       cmd_trb);
        /* Is the command ring deq ptr out of sync with the deq seg ptr? */
        if (cmd_dequeue_dma == 0) {
                xhci->error_bitmask |= 1 << 4;
@@ -1412,19 +1528,17 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
                return;
        }
 
-       trace_xhci_cmd_completion(&xhci->cmd_ring->dequeue->generic,
-                                       (struct xhci_generic_trb *) event);
+       trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
 
-       if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) ||
-               (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) {
+       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,
-                               GET_COMP_CODE(le32_to_cpu(event->status)))) {
+               if (handle_stopped_cmd_ring(xhci, cmd_comp_code)) {
                        inc_deq(xhci, xhci->cmd_ring);
                        return;
                }
@@ -1436,117 +1550,47 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
                        return;
        }
 
-       switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
-               & TRB_TYPE_BITMASK) {
-       case TRB_TYPE(TRB_ENABLE_SLOT):
-               if (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_SUCCESS)
-                       xhci->slot_id = slot_id;
-               else
-                       xhci->slot_id = 0;
-               complete(&xhci->addr_dev);
+       cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
+       switch (cmd_type) {
+       case TRB_ENABLE_SLOT:
+               xhci_handle_cmd_enable_slot(xhci, slot_id, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_DISABLE_SLOT):
-               if (xhci->devs[slot_id]) {
-                       if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
-                               /* Delete default control endpoint resources */
-                               xhci_free_device_endpoint_resources(xhci,
-                                               xhci->devs[slot_id], true);
-                       xhci_free_virt_device(xhci, slot_id);
-               }
+       case TRB_DISABLE_SLOT:
+               xhci_handle_cmd_disable_slot(xhci, slot_id);
                break;
-       case TRB_TYPE(TRB_CONFIG_EP):
-               virt_dev = xhci->devs[slot_id];
-               if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
-                       break;
-               /*
-                * Configure endpoint commands can come from the USB core
-                * configuration or alt setting changes, or because the HW
-                * needed an extra configure endpoint command after a reset
-                * endpoint command or streams were being configured.
-                * If the command was for a halted endpoint, the xHCI driver
-                * is not waiting on the configure endpoint command.
-                */
-               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");
-                       break;
-               }
-               /* Input ctx add_flags are the endpoint index plus one */
-               ep_index = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)) - 1;
-               /* A usb_set_interface() call directly after clearing a halted
-                * condition may race on this quirky hardware.  Not worth
-                * worrying about, since this is prototype hardware.  Not sure
-                * if this will work for streams, but streams support was
-                * untested on this prototype.
-                */
-               if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
-                               ep_index != (unsigned int) -1 &&
-                   le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG ==
-                   le32_to_cpu(ctrl_ctx->drop_flags)) {
-                       ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
-                       ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
-                       if (!(ep_state & EP_HALTED))
-                               goto bandwidth_change;
-                       xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
-                                       "Completed config ep cmd - "
-                                       "last ep index = %d, state = %d",
-                                       ep_index, ep_state);
-                       /* Clear internal halted state and restart ring(s) */
-                       xhci->devs[slot_id]->eps[ep_index].ep_state &=
-                               ~EP_HALTED;
-                       ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
-                       break;
-               }
-bandwidth_change:
-               xhci_dbg_trace(xhci,  trace_xhci_dbg_context_change,
-                               "Completed config ep cmd");
-               xhci->devs[slot_id]->cmd_status =
-                       GET_COMP_CODE(le32_to_cpu(event->status));
-               complete(&xhci->devs[slot_id]->cmd_completion);
+       case TRB_CONFIG_EP:
+               xhci_handle_cmd_config_ep(xhci, slot_id, event, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_EVAL_CONTEXT):
-               virt_dev = xhci->devs[slot_id];
-               if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
-                       break;
-               xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status));
-               complete(&xhci->devs[slot_id]->cmd_completion);
+       case TRB_EVAL_CONTEXT:
+               xhci_handle_cmd_eval_ctx(xhci, slot_id, event, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_ADDR_DEV):
-               xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status));
-               complete(&xhci->addr_dev);
+       case TRB_ADDR_DEV:
+               xhci_handle_cmd_addr_dev(xhci, slot_id, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_STOP_RING):
-               handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue, event);
+       case TRB_STOP_RING:
+               WARN_ON(slot_id != TRB_TO_SLOT_ID(
+                               le32_to_cpu(cmd_trb->generic.field[3])));
+               xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event);
                break;
-       case TRB_TYPE(TRB_SET_DEQ):
-               handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue);
+       case TRB_SET_DEQ:
+               WARN_ON(slot_id != TRB_TO_SLOT_ID(
+                               le32_to_cpu(cmd_trb->generic.field[3])));
+               xhci_handle_cmd_set_deq(xhci, slot_id, cmd_trb, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_CMD_NOOP):
+       case TRB_CMD_NOOP:
                break;
-       case TRB_TYPE(TRB_RESET_EP):
-               handle_reset_ep_completion(xhci, event, xhci->cmd_ring->dequeue);
+       case TRB_RESET_EP:
+               WARN_ON(slot_id != TRB_TO_SLOT_ID(
+                               le32_to_cpu(cmd_trb->generic.field[3])));
+               xhci_handle_cmd_reset_ep(xhci, slot_id, cmd_trb, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_RESET_DEV):
-               xhci_dbg(xhci, "Completed reset device command.\n");
-               slot_id = TRB_TO_SLOT_ID(
-                       le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]));
-               virt_dev = xhci->devs[slot_id];
-               if (virt_dev)
-                       handle_cmd_in_cmd_wait_list(xhci, virt_dev, event);
-               else
-                       xhci_warn(xhci, "Reset device command completion "
-                                       "for disabled slot %u\n", slot_id);
+       case TRB_RESET_DEV:
+               WARN_ON(slot_id != TRB_TO_SLOT_ID(
+                               le32_to_cpu(cmd_trb->generic.field[3])));
+               xhci_handle_cmd_reset_dev(xhci, slot_id, event);
                break;
-       case TRB_TYPE(TRB_NEC_GET_FW):
-               if (!(xhci->quirks & XHCI_NEC_HOST)) {
-                       xhci->error_bitmask |= 1 << 6;
-                       break;
-               }
-               xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
-                       "NEC firmware version %2x.%02x",
-                        NEC_FW_MAJOR(le32_to_cpu(event->status)),
-                        NEC_FW_MINOR(le32_to_cpu(event->status)));
+       case TRB_NEC_GET_FW:
+               xhci_handle_cmd_nec_get_fw(xhci, event);
                break;
        default:
                /* Skip over unknown commands on the event ring */
index 6e0d886bcce52c19361d321cf68c0abacf5b054c..4265b48856f6a7b0160124964b3b7fc697ecd826 100644 (file)
@@ -3459,7 +3459,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
        /* Wait for the Reset Device command to finish */
        timeleft = wait_for_completion_interruptible_timeout(
                        reset_device_cmd->completion,
-                       USB_CTRL_SET_TIMEOUT);
+                       XHCI_CMD_DEFAULT_TIMEOUT);
        if (timeleft <= 0) {
                xhci_warn(xhci, "%s while waiting for reset device command\n",
                                timeleft == 0 ? "Timeout" : "Signal");
@@ -3583,11 +3583,6 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
                del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
        }
 
-       if (udev->usb2_hw_lpm_enabled) {
-               xhci_set_usb2_hardware_lpm(hcd, udev, 0);
-               udev->usb2_hw_lpm_enabled = 0;
-       }
-
        spin_lock_irqsave(&xhci->lock, flags);
        /* Don't disable the slot if the host controller is dead. */
        state = xhci_readl(xhci, &xhci->op_regs->status);
@@ -3721,9 +3716,6 @@ disable_slot:
  * the device).
  * We should be protected by the usb_address0_mutex in khubd's hub_port_init, so
  * we should only issue and wait on one address command at the same time.
- *
- * We add one to the device address issued by the hardware because the USB core
- * uses address 1 for the root hubs (even though they're not really devices).
  */
 int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
@@ -3868,16 +3860,13 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
        slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
        trace_xhci_address_ctx(xhci, virt_dev->out_ctx,
                                slot_ctx->dev_info >> 27);
-       /* Use kernel assigned address for devices; store xHC assigned
-        * address locally. */
-       virt_dev->address = (le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK)
-               + 1;
        /* Zero the input context control for later use */
        ctrl_ctx->add_flags = 0;
        ctrl_ctx->drop_flags = 0;
 
        xhci_dbg_trace(xhci, trace_xhci_dbg_address,
-                       "Internal device address = %d", virt_dev->address);
+                      "Internal device address = %d",
+                      le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
 
        return 0;
 }
@@ -4025,133 +4014,6 @@ static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev)
        return PORT_BESLD(besld) | PORT_L1_TIMEOUT(l1) | PORT_HIRDM(hirdm);
 }
 
-static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
-                                       struct usb_device *udev)
-{
-       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       struct dev_info *dev_info;
-       __le32 __iomem  **port_array;
-       __le32 __iomem  *addr, *pm_addr;
-       u32             temp, dev_id;
-       unsigned int    port_num;
-       unsigned long   flags;
-       int             hird;
-       int             ret;
-
-       if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
-                       !udev->lpm_capable)
-               return -EINVAL;
-
-       /* we only support lpm for non-hub device connected to root hub yet */
-       if (!udev->parent || udev->parent->parent ||
-                       udev->descriptor.bDeviceClass == USB_CLASS_HUB)
-               return -EINVAL;
-
-       spin_lock_irqsave(&xhci->lock, flags);
-
-       /* Look for devices in lpm_failed_devs list */
-       dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 |
-                       le16_to_cpu(udev->descriptor.idProduct);
-       list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) {
-               if (dev_info->dev_id == dev_id) {
-                       ret = -EINVAL;
-                       goto finish;
-               }
-       }
-
-       port_array = xhci->usb2_ports;
-       port_num = udev->portnum - 1;
-
-       if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) {
-               xhci_dbg(xhci, "invalid port number %d\n", udev->portnum);
-               ret = -EINVAL;
-               goto finish;
-       }
-
-       /*
-        * Test USB 2.0 software LPM.
-        * FIXME: some xHCI 1.0 hosts may implement a new register to set up
-        * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1
-        * in the June 2011 errata release.
-        */
-       xhci_dbg(xhci, "test port %d software LPM\n", port_num);
-       /*
-        * Set L1 Device Slot and HIRD/BESL.
-        * Check device's USB 2.0 extension descriptor to determine whether
-        * HIRD or BESL shoule be used. See USB2.0 LPM errata.
-        */
-       pm_addr = port_array[port_num] + PORTPMSC;
-       hird = xhci_calculate_hird_besl(xhci, udev);
-       temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
-       xhci_writel(xhci, temp, pm_addr);
-
-       /* Set port link state to U2(L1) */
-       addr = port_array[port_num];
-       xhci_set_link_state(xhci, port_array, port_num, XDEV_U2);
-
-       /* wait for ACK */
-       spin_unlock_irqrestore(&xhci->lock, flags);
-       msleep(10);
-       spin_lock_irqsave(&xhci->lock, flags);
-
-       /* Check L1 Status */
-       ret = xhci_handshake(xhci, pm_addr,
-                       PORT_L1S_MASK, PORT_L1S_SUCCESS, 125);
-       if (ret != -ETIMEDOUT) {
-               /* enter L1 successfully */
-               temp = xhci_readl(xhci, addr);
-               xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n",
-                               port_num, temp);
-               ret = 0;
-       } else {
-               temp = xhci_readl(xhci, pm_addr);
-               xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n",
-                               port_num, temp & PORT_L1S_MASK);
-               ret = -EINVAL;
-       }
-
-       /* Resume the port */
-       xhci_set_link_state(xhci, port_array, port_num, XDEV_U0);
-
-       spin_unlock_irqrestore(&xhci->lock, flags);
-       msleep(10);
-       spin_lock_irqsave(&xhci->lock, flags);
-
-       /* Clear PLC */
-       xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC);
-
-       /* Check PORTSC to make sure the device is in the right state */
-       if (!ret) {
-               temp = xhci_readl(xhci, addr);
-               xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp);
-               if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) ||
-                               (temp & PORT_PLS_MASK) != XDEV_U0) {
-                       xhci_dbg(xhci, "port L1 resume fail\n");
-                       ret = -EINVAL;
-               }
-       }
-
-       if (ret) {
-               /* Insert dev to lpm_failed_devs list */
-               xhci_warn(xhci, "device LPM test failed, may disconnect and "
-                               "re-enumerate\n");
-               dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC);
-               if (!dev_info) {
-                       ret = -ENOMEM;
-                       goto finish;
-               }
-               dev_info->dev_id = dev_id;
-               INIT_LIST_HEAD(&dev_info->list);
-               list_add(&dev_info->list, &xhci->lpm_failed_devs);
-       } else {
-               xhci_ring_device(xhci, udev->slot_id);
-       }
-
-finish:
-       spin_unlock_irqrestore(&xhci->lock, flags);
-       return ret;
-}
-
 int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
                        struct usb_device *udev, int enable)
 {
@@ -4228,7 +4090,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
                }
 
                pm_val &= ~PORT_HIRD_MASK;
-               pm_val |= PORT_HIRD(hird) | PORT_RWE;
+               pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id);
                xhci_writel(xhci, pm_val, pm_addr);
                pm_val = xhci_readl(xhci, pm_addr);
                pm_val |= PORT_HLE;
@@ -4236,7 +4098,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
                /* flush write */
                xhci_readl(xhci, pm_addr);
        } else {
-               pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK);
+               pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK);
                xhci_writel(xhci, pm_val, pm_addr);
                /* flush write */
                xhci_readl(xhci, pm_addr);
@@ -4279,24 +4141,26 @@ static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port,
 int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       int             ret;
        int             portnum = udev->portnum - 1;
 
-       ret = xhci_usb2_software_lpm_test(hcd, udev);
-       if (!ret) {
-               xhci_dbg(xhci, "software LPM test succeed\n");
-               if (xhci->hw_lpm_support == 1 &&
-                   xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) {
-                       udev->usb2_hw_lpm_capable = 1;
-                       udev->l1_params.timeout = XHCI_L1_TIMEOUT;
-                       udev->l1_params.besl = XHCI_DEFAULT_BESL;
-                       if (xhci_check_usb2_port_capability(xhci, portnum,
-                                                           XHCI_BLC))
-                               udev->usb2_hw_lpm_besl_capable = 1;
-                       ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1);
-                       if (!ret)
-                               udev->usb2_hw_lpm_enabled = 1;
-               }
+       if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
+                       !udev->lpm_capable)
+               return 0;
+
+       /* we only support lpm for non-hub device connected to root hub yet */
+       if (!udev->parent || udev->parent->parent ||
+                       udev->descriptor.bDeviceClass == USB_CLASS_HUB)
+               return 0;
+
+       if (xhci->hw_lpm_support == 1 &&
+                       xhci_check_usb2_port_capability(
+                               xhci, portnum, XHCI_HLC)) {
+               udev->usb2_hw_lpm_capable = 1;
+               udev->l1_params.timeout = XHCI_L1_TIMEOUT;
+               udev->l1_params.besl = XHCI_DEFAULT_BESL;
+               if (xhci_check_usb2_port_capability(xhci, portnum,
+                                       XHCI_BLC))
+                       udev->usb2_hw_lpm_besl_capable = 1;
        }
 
        return 0;
index 941d5f59e4dcc254770bac770ba024e36a677bad..03c74b7965f85d767436c8108348f6adf6f87126 100644 (file)
@@ -383,6 +383,7 @@ struct xhci_op_regs {
 #define        PORT_RWE                (1 << 3)
 #define        PORT_HIRD(p)            (((p) & 0xf) << 4)
 #define        PORT_HIRD_MASK          (0xf << 4)
+#define        PORT_L1DS_MASK          (0xff << 8)
 #define        PORT_L1DS(p)            (((p) & 0xff) << 8)
 #define        PORT_HLE                (1 << 16)
 
@@ -934,8 +935,6 @@ struct xhci_virt_device {
        /* Rings saved to ensure old alt settings can be re-instated */
        struct xhci_ring                **ring_cache;
        int                             num_rings_cached;
-       /* Store xHC assigned device address */
-       int                             address;
 #define        XHCI_MAX_RINGS_CACHED   31
        struct xhci_virt_ep             eps[31];
        struct completion               cmd_completion;
index aa28ac8c7607deeb49136340f0def4dd5f6fa05d..b4152820d655f389c9c491b4ead7c6c159e58572 100644 (file)
@@ -120,7 +120,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
                        struct usb_host_endpoint        *e;
 
                        e = alt->endpoint + ep;
-                       switch (e->desc.bmAttributes) {
+                       switch (usb_endpoint_type(&e->desc)) {
                        case USB_ENDPOINT_XFER_BULK:
                                break;
                        case USB_ENDPOINT_XFER_ISOC:
@@ -437,7 +437,7 @@ alloc_sglist(int nents, int max, int vary)
        if (max == 0)
                return NULL;
 
-       sg = kmalloc_array(nents, sizeof *sg, GFP_KERNEL);
+       sg = kmalloc_array(nents, sizeof(*sg), GFP_KERNEL);
        if (!sg)
                return NULL;
        sg_init_table(sg, nents);
@@ -573,7 +573,7 @@ static int is_good_config(struct usbtest_dev *tdev, int len)
 {
        struct usb_config_descriptor    *config;
 
-       if (len < sizeof *config)
+       if (len < sizeof(*config))
                return 0;
        config = (struct usb_config_descriptor *) tdev->buf;
 
@@ -606,6 +606,76 @@ static int is_good_config(struct usbtest_dev *tdev, int len)
        return 0;
 }
 
+static int is_good_ext(struct usbtest_dev *tdev, u8 *buf)
+{
+       struct usb_ext_cap_descriptor *ext;
+       u32 attr;
+
+       ext = (struct usb_ext_cap_descriptor *) buf;
+
+       if (ext->bLength != USB_DT_USB_EXT_CAP_SIZE) {
+               ERROR(tdev, "bogus usb 2.0 extension descriptor length\n");
+               return 0;
+       }
+
+       attr = le32_to_cpu(ext->bmAttributes);
+       /* bits[1:4] is used and others are reserved */
+       if (attr & ~0x1e) {     /* reserved == 0 */
+               ERROR(tdev, "reserved bits set\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int is_good_ss_cap(struct usbtest_dev *tdev, u8 *buf)
+{
+       struct usb_ss_cap_descriptor *ss;
+
+       ss = (struct usb_ss_cap_descriptor *) buf;
+
+       if (ss->bLength != USB_DT_USB_SS_CAP_SIZE) {
+               ERROR(tdev, "bogus superspeed device capability descriptor length\n");
+               return 0;
+       }
+
+       /*
+        * only bit[1] of bmAttributes is used for LTM and others are
+        * reserved
+        */
+       if (ss->bmAttributes & ~0x02) { /* reserved == 0 */
+               ERROR(tdev, "reserved bits set in bmAttributes\n");
+               return 0;
+       }
+
+       /* bits[0:3] of wSpeedSupported is used and others are reserved */
+       if (le16_to_cpu(ss->wSpeedSupported) & ~0x0f) { /* reserved == 0 */
+               ERROR(tdev, "reserved bits set in wSpeedSupported\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int is_good_con_id(struct usbtest_dev *tdev, u8 *buf)
+{
+       struct usb_ss_container_id_descriptor *con_id;
+
+       con_id = (struct usb_ss_container_id_descriptor *) buf;
+
+       if (con_id->bLength != USB_DT_USB_SS_CONTN_ID_SIZE) {
+               ERROR(tdev, "bogus container id descriptor length\n");
+               return 0;
+       }
+
+       if (con_id->bReserved) {        /* reserved == 0 */
+               ERROR(tdev, "reserved bits set\n");
+               return 0;
+       }
+
+       return 1;
+}
+
 /* sanity test for standard requests working with usb_control_mesg() and some
  * of the utility functions which use it.
  *
@@ -683,12 +753,96 @@ static int ch9_postconfig(struct usbtest_dev *dev)
 
        /* there's always [9.4.3] a device descriptor [9.6.1] */
        retval = usb_get_descriptor(udev, USB_DT_DEVICE, 0,
-                       dev->buf, sizeof udev->descriptor);
-       if (retval != sizeof udev->descriptor) {
+                       dev->buf, sizeof(udev->descriptor));
+       if (retval != sizeof(udev->descriptor)) {
                dev_err(&iface->dev, "dev descriptor --> %d\n", retval);
                return (retval < 0) ? retval : -EDOM;
        }
 
+       /*
+        * there's always [9.4.3] a bos device descriptor [9.6.2] in USB
+        * 3.0 spec
+        */
+       if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0300) {
+               struct usb_bos_descriptor *bos = NULL;
+               struct usb_dev_cap_header *header = NULL;
+               unsigned total, num, length;
+               u8 *buf;
+
+               retval = usb_get_descriptor(udev, USB_DT_BOS, 0, dev->buf,
+                               sizeof(*udev->bos->desc));
+               if (retval != sizeof(*udev->bos->desc)) {
+                       dev_err(&iface->dev, "bos descriptor --> %d\n", retval);
+                       return (retval < 0) ? retval : -EDOM;
+               }
+
+               bos = (struct usb_bos_descriptor *)dev->buf;
+               total = le16_to_cpu(bos->wTotalLength);
+               num = bos->bNumDeviceCaps;
+
+               if (total > TBUF_SIZE)
+                       total = TBUF_SIZE;
+
+               /*
+                * get generic device-level capability descriptors [9.6.2]
+                * in USB 3.0 spec
+                */
+               retval = usb_get_descriptor(udev, USB_DT_BOS, 0, dev->buf,
+                               total);
+               if (retval != total) {
+                       dev_err(&iface->dev, "bos descriptor set --> %d\n",
+                                       retval);
+                       return (retval < 0) ? retval : -EDOM;
+               }
+
+               length = sizeof(*udev->bos->desc);
+               buf = dev->buf;
+               for (i = 0; i < num; i++) {
+                       buf += length;
+                       if (buf + sizeof(struct usb_dev_cap_header) >
+                                       dev->buf + total)
+                               break;
+
+                       header = (struct usb_dev_cap_header *)buf;
+                       length = header->bLength;
+
+                       if (header->bDescriptorType !=
+                                       USB_DT_DEVICE_CAPABILITY) {
+                               dev_warn(&udev->dev, "not device capability descriptor, skip\n");
+                               continue;
+                       }
+
+                       switch (header->bDevCapabilityType) {
+                       case USB_CAP_TYPE_EXT:
+                               if (buf + USB_DT_USB_EXT_CAP_SIZE >
+                                               dev->buf + total ||
+                                               !is_good_ext(dev, buf)) {
+                                       dev_err(&iface->dev, "bogus usb 2.0 extension descriptor\n");
+                                       return -EDOM;
+                               }
+                               break;
+                       case USB_SS_CAP_TYPE:
+                               if (buf + USB_DT_USB_SS_CAP_SIZE >
+                                               dev->buf + total ||
+                                               !is_good_ss_cap(dev, buf)) {
+                                       dev_err(&iface->dev, "bogus superspeed device capability descriptor\n");
+                                       return -EDOM;
+                               }
+                               break;
+                       case CONTAINER_ID_TYPE:
+                               if (buf + USB_DT_USB_SS_CONTN_ID_SIZE >
+                                               dev->buf + total ||
+                                               !is_good_con_id(dev, buf)) {
+                                       dev_err(&iface->dev, "bogus container id descriptor\n");
+                                       return -EDOM;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+
        /* there's always [9.4.3] at least one config descriptor [9.6.3] */
        for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
                retval = usb_get_descriptor(udev, USB_DT_CONFIG, i,
@@ -954,7 +1108,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
                 * device, but some are chosen to trigger protocol stalls
                 * or short reads.
                 */
-               memset(&req, 0, sizeof req);
+               memset(&req, 0, sizeof(req));
                req.bRequest = USB_REQ_GET_DESCRIPTOR;
                req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE;
 
@@ -1074,7 +1228,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
                if (!u)
                        goto cleanup;
 
-               reqp = kmalloc(sizeof *reqp, GFP_KERNEL);
+               reqp = kmalloc(sizeof(*reqp), GFP_KERNEL);
                if (!reqp)
                        goto cleanup;
                reqp->setup = req;
@@ -1667,13 +1821,13 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
        if (param->sglen > 10)
                return -EDOM;
 
-       memset(&context, 0, sizeof context);
+       memset(&context, 0, sizeof(context));
        context.count = param->iterations * param->sglen;
        context.dev = dev;
        init_completion(&context.done);
        spin_lock_init(&context.lock);
 
-       memset(urbs, 0, sizeof urbs);
+       memset(urbs, 0, sizeof(urbs));
        udev = testdev_to_usbdev(dev);
        dev_info(&dev->intf->dev,
                "... iso period %d %sframes, wMaxPacket %04x\n",
index c258a97ef1b0050f41e02d0a8e08b0052af488e7..57dfc0cedb0055cfb28078b7d4da03d42abb37b0 100644 (file)
@@ -75,6 +75,7 @@ config USB_MUSB_TUSB6010
 config USB_MUSB_OMAP2PLUS
        tristate "OMAP2430 and onwards"
        depends on ARCH_OMAP2PLUS
+       select GENERIC_PHY
 
 config USB_MUSB_AM35X
        tristate "AM35x"
@@ -90,7 +91,7 @@ config USB_MUSB_BLACKFIN
        depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
 
 config USB_MUSB_UX500
-       tristate "U8500 and U5500"
+       tristate "Ux500 platforms"
 
 endchoice
 
@@ -112,7 +113,7 @@ choice
          allow using DMA on multiplatform kernels.
 
 config USB_UX500_DMA
-       bool 'ST Ericsson U8500 and U5500'
+       bool 'ST Ericsson Ux500'
        depends on USB_MUSB_UX500
        help
          Enable DMA transfers on UX500 platforms.
index 5c310c664218dec6d1e71874ee8dc84f0232fa19..ca45b39db5b91b62d36e61089916ce3910f7af89 100644 (file)
@@ -89,7 +89,6 @@ struct am35x_glue {
        struct clk              *phy_clk;
        struct clk              *clk;
 };
-#define glue_to_musb(g)                platform_get_drvdata(g->musb)
 
 /*
  * am35x_musb_enable - enable interrupts
@@ -452,14 +451,18 @@ static const struct musb_platform_ops am35x_ops = {
        .set_vbus       = am35x_musb_set_vbus,
 };
 
-static u64 am35x_dmamask = DMA_BIT_MASK(32);
+static const struct platform_device_info am35x_dev_info = {
+       .name           = "musb-hdrc",
+       .id             = PLATFORM_DEVID_AUTO,
+       .dma_mask       = DMA_BIT_MASK(32),
+};
 
 static int am35x_probe(struct platform_device *pdev)
 {
        struct musb_hdrc_platform_data  *pdata = dev_get_platdata(&pdev->dev);
        struct platform_device          *musb;
        struct am35x_glue               *glue;
-
+       struct platform_device_info     pinfo;
        struct clk                      *phy_clk;
        struct clk                      *clk;
 
@@ -471,12 +474,6 @@ static int am35x_probe(struct platform_device *pdev)
                goto err0;
        }
 
-       musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
-       if (!musb) {
-               dev_err(&pdev->dev, "failed to allocate musb device\n");
-               goto err1;
-       }
-
        phy_clk = clk_get(&pdev->dev, "fck");
        if (IS_ERR(phy_clk)) {
                dev_err(&pdev->dev, "failed to get PHY clock\n");
@@ -503,12 +500,7 @@ static int am35x_probe(struct platform_device *pdev)
                goto err6;
        }
 
-       musb->dev.parent                = &pdev->dev;
-       musb->dev.dma_mask              = &am35x_dmamask;
-       musb->dev.coherent_dma_mask     = am35x_dmamask;
-
        glue->dev                       = &pdev->dev;
-       glue->musb                      = musb;
        glue->phy_clk                   = phy_clk;
        glue->clk                       = clk;
 
@@ -516,22 +508,17 @@ static int am35x_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, glue);
 
-       ret = platform_device_add_resources(musb, pdev->resource,
-                       pdev->num_resources);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add resources\n");
-               goto err7;
-       }
-
-       ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add platform_data\n");
-               goto err7;
-       }
-
-       ret = platform_device_add(musb);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register musb device\n");
+       pinfo = am35x_dev_info;
+       pinfo.parent = &pdev->dev;
+       pinfo.res = pdev->resource;
+       pinfo.num_res = pdev->num_resources;
+       pinfo.data = pdata;
+       pinfo.size_data = sizeof(*pdata);
+
+       glue->musb = musb = platform_device_register_full(&pinfo);
+       if (IS_ERR(musb)) {
+               ret = PTR_ERR(musb);
+               dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
                goto err7;
        }
 
@@ -550,9 +537,6 @@ err4:
        clk_put(phy_clk);
 
 err3:
-       platform_device_put(musb);
-
-err1:
        kfree(glue);
 
 err0:
@@ -615,23 +599,16 @@ static int am35x_resume(struct device *dev)
 
        return 0;
 }
-
-static struct dev_pm_ops am35x_pm_ops = {
-       .suspend        = am35x_suspend,
-       .resume         = am35x_resume,
-};
-
-#define DEV_PM_OPS     &am35x_pm_ops
-#else
-#define DEV_PM_OPS     NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(am35x_pm_ops, am35x_suspend, am35x_resume);
+
 static struct platform_driver am35x_driver = {
        .probe          = am35x_probe,
        .remove         = am35x_remove,
        .driver         = {
                .name   = "musb-am35x",
-               .pm     = DEV_PM_OPS,
+               .pm     = &am35x_pm_ops,
        },
 };
 
index 72e2056b6082f4c4872b9a7d94c82f59193e00f4..d9692f78e227ead7e79b28becdafda6c275161d8 100644 (file)
@@ -561,23 +561,16 @@ static int bfin_resume(struct device *dev)
 
        return 0;
 }
-
-static struct dev_pm_ops bfin_pm_ops = {
-       .suspend        = bfin_suspend,
-       .resume         = bfin_resume,
-};
-
-#define DEV_PM_OPS     &bfin_pm_ops
-#else
-#define DEV_PM_OPS     NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume);
+
 static struct platform_driver bfin_driver = {
        .probe          = bfin_probe,
        .remove         = __exit_p(bfin_remove),
        .driver         = {
                .name   = "musb-blackfin",
-               .pm     = DEV_PM_OPS,
+               .pm     = &bfin_pm_ops,
        },
 };
 
index d9ddf4122f37e6ddfd19661eb9cb1448306525b6..2f2c1cb364218833f40468429d1469299a56e406 100644 (file)
@@ -472,7 +472,11 @@ static const struct musb_platform_ops da8xx_ops = {
        .set_vbus       = da8xx_musb_set_vbus,
 };
 
-static u64 da8xx_dmamask = DMA_BIT_MASK(32);
+static const struct platform_device_info da8xx_dev_info = {
+       .name           = "musb-hdrc",
+       .id             = PLATFORM_DEVID_AUTO,
+       .dma_mask       = DMA_BIT_MASK(32),
+};
 
 static int da8xx_probe(struct platform_device *pdev)
 {
@@ -480,7 +484,7 @@ static int da8xx_probe(struct platform_device *pdev)
        struct musb_hdrc_platform_data  *pdata = dev_get_platdata(&pdev->dev);
        struct platform_device          *musb;
        struct da8xx_glue               *glue;
-
+       struct platform_device_info     pinfo;
        struct clk                      *clk;
 
        int                             ret = -ENOMEM;
@@ -491,12 +495,6 @@ static int da8xx_probe(struct platform_device *pdev)
                goto err0;
        }
 
-       musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
-       if (!musb) {
-               dev_err(&pdev->dev, "failed to allocate musb device\n");
-               goto err1;
-       }
-
        clk = clk_get(&pdev->dev, "usb20");
        if (IS_ERR(clk)) {
                dev_err(&pdev->dev, "failed to get clock\n");
@@ -510,12 +508,7 @@ static int da8xx_probe(struct platform_device *pdev)
                goto err4;
        }
 
-       musb->dev.parent                = &pdev->dev;
-       musb->dev.dma_mask              = &da8xx_dmamask;
-       musb->dev.coherent_dma_mask     = da8xx_dmamask;
-
        glue->dev                       = &pdev->dev;
-       glue->musb                      = musb;
        glue->clk                       = clk;
 
        pdata->platform_ops             = &da8xx_ops;
@@ -535,22 +528,17 @@ static int da8xx_probe(struct platform_device *pdev)
        musb_resources[1].end = pdev->resource[1].end;
        musb_resources[1].flags = pdev->resource[1].flags;
 
-       ret = platform_device_add_resources(musb, musb_resources,
-                       ARRAY_SIZE(musb_resources));
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add resources\n");
-               goto err5;
-       }
-
-       ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add platform_data\n");
-               goto err5;
-       }
-
-       ret = platform_device_add(musb);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register musb device\n");
+       pinfo = da8xx_dev_info;
+       pinfo.parent = &pdev->dev;
+       pinfo.res = musb_resources;
+       pinfo.num_res = ARRAY_SIZE(musb_resources);
+       pinfo.data = pdata;
+       pinfo.size_data = sizeof(*pdata);
+
+       glue->musb = musb = platform_device_register_full(&pinfo);
+       if (IS_ERR(musb)) {
+               ret = PTR_ERR(musb);
+               dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
                goto err5;
        }
 
@@ -563,9 +551,6 @@ err4:
        clk_put(clk);
 
 err3:
-       platform_device_put(musb);
-
-err1:
        kfree(glue);
 
 err0:
index ed0834e2b72eeaa6c9dbe73bdb77c2f433282b42..1121fd741bf8a81e3c4f7a7fc5598dd1dddb948b 100644 (file)
@@ -505,14 +505,19 @@ static const struct musb_platform_ops davinci_ops = {
        .set_vbus       = davinci_musb_set_vbus,
 };
 
-static u64 davinci_dmamask = DMA_BIT_MASK(32);
+static const struct platform_device_info davinci_dev_info = {
+       .name           = "musb-hdrc",
+       .id             = PLATFORM_DEVID_AUTO,
+       .dma_mask       = DMA_BIT_MASK(32),
+};
 
 static int davinci_probe(struct platform_device *pdev)
 {
-       struct resource musb_resources[2];
+       struct resource                 musb_resources[3];
        struct musb_hdrc_platform_data  *pdata = dev_get_platdata(&pdev->dev);
        struct platform_device          *musb;
        struct davinci_glue             *glue;
+       struct platform_device_info     pinfo;
        struct clk                      *clk;
 
        int                             ret = -ENOMEM;
@@ -523,12 +528,6 @@ static int davinci_probe(struct platform_device *pdev)
                goto err0;
        }
 
-       musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
-       if (!musb) {
-               dev_err(&pdev->dev, "failed to allocate musb device\n");
-               goto err1;
-       }
-
        clk = clk_get(&pdev->dev, "usb");
        if (IS_ERR(clk)) {
                dev_err(&pdev->dev, "failed to get clock\n");
@@ -542,12 +541,7 @@ static int davinci_probe(struct platform_device *pdev)
                goto err4;
        }
 
-       musb->dev.parent                = &pdev->dev;
-       musb->dev.dma_mask              = &davinci_dmamask;
-       musb->dev.coherent_dma_mask     = davinci_dmamask;
-
        glue->dev                       = &pdev->dev;
-       glue->musb                      = musb;
        glue->clk                       = clk;
 
        pdata->platform_ops             = &davinci_ops;
@@ -567,22 +561,26 @@ static int davinci_probe(struct platform_device *pdev)
        musb_resources[1].end = pdev->resource[1].end;
        musb_resources[1].flags = pdev->resource[1].flags;
 
-       ret = platform_device_add_resources(musb, musb_resources,
-                       ARRAY_SIZE(musb_resources));
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add resources\n");
-               goto err5;
-       }
-
-       ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add platform_data\n");
-               goto err5;
-       }
-
-       ret = platform_device_add(musb);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register musb device\n");
+       /*
+        * For DM6467 3 resources are passed. A placeholder for the 3rd
+        * resource is always there, so it's safe to always copy it...
+        */
+       musb_resources[2].name = pdev->resource[2].name;
+       musb_resources[2].start = pdev->resource[2].start;
+       musb_resources[2].end = pdev->resource[2].end;
+       musb_resources[2].flags = pdev->resource[2].flags;
+
+       pinfo = davinci_dev_info;
+       pinfo.parent = &pdev->dev;
+       pinfo.res = musb_resources;
+       pinfo.num_res = ARRAY_SIZE(musb_resources);
+       pinfo.data = pdata;
+       pinfo.size_data = sizeof(*pdata);
+
+       glue->musb = musb = platform_device_register_full(&pinfo);
+       if (IS_ERR(musb)) {
+               ret = PTR_ERR(musb);
+               dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
                goto err5;
        }
 
@@ -595,9 +593,6 @@ err4:
        clk_put(clk);
 
 err3:
-       platform_device_put(musb);
-
-err1:
        kfree(glue);
 
 err0:
index 41ac5b5b57ceadfdfa5a796c51c4eed445f0f9f1..8be9b02c3cc2a37dce87eaab685a10c1a58d6632 100644 (file)
@@ -46,7 +46,7 @@ static struct platform_driver am335x_child_driver = {
        .remove         = am335x_child_remove,
        .driver         = {
                .name   = "am335x-usb-childs",
-               .of_match_table = of_match_ptr(am335x_child_of_match),
+               .of_match_table = am335x_child_of_match,
        },
 };
 
index cd70cc8861711015f5443e2b76e969209d9bdb3a..0a43329569d178de72a5c8ee619e7206c360c51c 100644 (file)
@@ -617,7 +617,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                /* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
                                default:
                                        s = "VALID"; break;
-                               }; s; }),
+                               } s; }),
                                VBUSERR_RETRY_COUNT - musb->vbuserr_retry,
                                musb->port1_status);
 
@@ -1809,8 +1809,7 @@ static void musb_free(struct musb *musb)
                        disable_irq_wake(musb->nIrq);
                free_irq(musb->nIrq, musb);
        }
-       if (musb->dma_controller)
-               dma_controller_destroy(musb->dma_controller);
+       cancel_work_sync(&musb->irq_work);
 
        musb_host_free(musb);
 }
@@ -1885,8 +1884,13 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
 
        pm_runtime_get_sync(musb->controller);
 
-       if (use_dma && dev->dma_mask)
+       if (use_dma && dev->dma_mask) {
                musb->dma_controller = dma_controller_create(musb, musb->mregs);
+               if (IS_ERR(musb->dma_controller)) {
+                       status = PTR_ERR(musb->dma_controller);
+                       goto fail2_5;
+               }
+       }
 
        /* be sure interrupts are disabled before connecting ISR */
        musb_platform_disable(musb);
@@ -1946,6 +1950,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
                if (status < 0)
                        goto fail3;
                status = musb_gadget_setup(musb);
+               if (status)
+                       musb_host_cleanup(musb);
                break;
        default:
                dev_err(dev, "unsupported port mode %d\n", musb->port_mode);
@@ -1972,10 +1978,12 @@ fail5:
 
 fail4:
        musb_gadget_cleanup(musb);
+       musb_host_cleanup(musb);
 
 fail3:
        if (musb->dma_controller)
                dma_controller_destroy(musb->dma_controller);
+fail2_5:
        pm_runtime_put_sync(musb->controller);
 
 fail2:
@@ -2032,6 +2040,9 @@ static int musb_remove(struct platform_device *pdev)
        musb_exit_debugfs(musb);
        musb_shutdown(pdev);
 
+       if (musb->dma_controller)
+               dma_controller_destroy(musb->dma_controller);
+
        musb_free(musb);
        device_init_wakeup(dev, 0);
        return 0;
index 1c5bf75ee8ff8a45a3da1fca7cb1d31920f0eeff..29f7cd7c7964e9bf78607de0031396393e84bd44 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/usb.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/musb.h>
+#include <linux/phy/phy.h>
 
 struct musb;
 struct musb_hw_ep;
@@ -341,6 +342,7 @@ struct musb {
        u16                     int_tx;
 
        struct usb_phy          *xceiv;
+       struct phy              *phy;
 
        int nIrq;
        unsigned                irq_wake:1;
index ae959746f77f04331a1d8dde9d1da57d90af24b8..ff9d6de2b7465c949d54ae4801108a3800f4be3f 100644 (file)
@@ -484,6 +484,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
                if (ret)
                        goto err;
 
+               ret = -EINVAL;
                if (port > MUSB_DMA_NUM_CHANNELS || !port)
                        goto err;
                if (is_tx)
@@ -503,6 +504,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
                dc = dma_request_slave_channel(dev, str);
                if (!dc) {
                        dev_err(dev, "Falied to request %s.\n", str);
+                       ret = -EPROBE_DEFER;
                        goto err;
                }
                cppi41_channel->dc = dc;
@@ -510,7 +512,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
        return 0;
 err:
        cppi41_release_all_dma_chans(controller);
-       return -EINVAL;
+       return ret;
 }
 
 void dma_controller_destroy(struct dma_controller *c)
@@ -526,7 +528,7 @@ struct dma_controller *dma_controller_create(struct musb *musb,
                                        void __iomem *base)
 {
        struct cppi41_dma_controller *controller;
-       int ret;
+       int ret = 0;
 
        if (!musb->controller->of_node) {
                dev_err(musb->controller, "Need DT for the DMA engine.\n");
@@ -553,5 +555,7 @@ struct dma_controller *dma_controller_create(struct musb *musb,
 plat_get_fail:
        kfree(controller);
 kzalloc_fail:
+       if (ret == -EPROBE_DEFER)
+               return ERR_PTR(ret);
        return NULL;
 }
index bd4138d80a48f243c9d4cb32527196d0024eb3d1..1901f6fe580749c93862910e322af543dd1eb8c7 100644 (file)
@@ -121,6 +121,43 @@ struct dsps_glue {
        unsigned long last_timer;    /* last timer data for each instance */
 };
 
+static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
+{
+       struct device *dev = musb->controller;
+       struct dsps_glue *glue = dev_get_drvdata(dev->parent);
+
+       if (timeout == 0)
+               timeout = jiffies + msecs_to_jiffies(3);
+
+       /* Never idle if active, or when VBUS timeout is not set as host */
+       if (musb->is_active || (musb->a_wait_bcon == 0 &&
+                               musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
+               dev_dbg(musb->controller, "%s active, deleting timer\n",
+                               usb_otg_state_string(musb->xceiv->state));
+               del_timer(&glue->timer);
+               glue->last_timer = jiffies;
+               return;
+       }
+       if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE)
+               return;
+
+       if (!musb->g.dev.driver)
+               return;
+
+       if (time_after(glue->last_timer, timeout) &&
+                               timer_pending(&glue->timer)) {
+               dev_dbg(musb->controller,
+                       "Longer idle timer already pending, ignoring...\n");
+               return;
+       }
+       glue->last_timer = timeout;
+
+       dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
+               usb_otg_state_string(musb->xceiv->state),
+                       jiffies_to_msecs(timeout - jiffies));
+       mod_timer(&glue->timer, timeout);
+}
+
 /**
  * dsps_musb_enable - enable interrupts
  */
@@ -143,6 +180,7 @@ static void dsps_musb_enable(struct musb *musb)
        /* Force the DRVVBUS IRQ so we can start polling for ID change. */
        dsps_writel(reg_base, wrp->coreintr_set,
                    (1 << wrp->drvvbus) << wrp->usb_shift);
+       dsps_musb_try_idle(musb, 0);
 }
 
 /**
@@ -171,6 +209,7 @@ static void otg_timer(unsigned long _musb)
        const struct dsps_musb_wrapper *wrp = glue->wrp;
        u8 devctl;
        unsigned long flags;
+       int skip_session = 0;
 
        /*
         * We poll because DSPS IP's won't expose several OTG-critical
@@ -183,10 +222,12 @@ static void otg_timer(unsigned long _musb)
        spin_lock_irqsave(&musb->lock, flags);
        switch (musb->xceiv->state) {
        case OTG_STATE_A_WAIT_BCON:
-               devctl &= ~MUSB_DEVCTL_SESSION;
-               dsps_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+               dsps_writeb(musb->mregs, MUSB_DEVCTL, 0);
+               skip_session = 1;
+               /* fall */
 
-               devctl = dsps_readb(musb->mregs, MUSB_DEVCTL);
+       case OTG_STATE_A_IDLE:
+       case OTG_STATE_B_IDLE:
                if (devctl & MUSB_DEVCTL_BDEVICE) {
                        musb->xceiv->state = OTG_STATE_B_IDLE;
                        MUSB_DEV_MODE(musb);
@@ -194,60 +235,21 @@ static void otg_timer(unsigned long _musb)
                        musb->xceiv->state = OTG_STATE_A_IDLE;
                        MUSB_HST_MODE(musb);
                }
+               if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
+                       dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
+               mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
                break;
        case OTG_STATE_A_WAIT_VFALL:
                musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
                dsps_writel(musb->ctrl_base, wrp->coreintr_set,
                            MUSB_INTR_VBUSERROR << wrp->usb_shift);
                break;
-       case OTG_STATE_B_IDLE:
-               devctl = dsps_readb(mregs, MUSB_DEVCTL);
-               if (devctl & MUSB_DEVCTL_BDEVICE)
-                       mod_timer(&glue->timer,
-                                       jiffies + wrp->poll_seconds * HZ);
-               else
-                       musb->xceiv->state = OTG_STATE_A_IDLE;
-               break;
        default:
                break;
        }
        spin_unlock_irqrestore(&musb->lock, flags);
 }
 
-static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
-{
-       struct device *dev = musb->controller;
-       struct dsps_glue *glue = dev_get_drvdata(dev->parent);
-
-       if (timeout == 0)
-               timeout = jiffies + msecs_to_jiffies(3);
-
-       /* Never idle if active, or when VBUS timeout is not set as host */
-       if (musb->is_active || (musb->a_wait_bcon == 0 &&
-                               musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
-               dev_dbg(musb->controller, "%s active, deleting timer\n",
-                               usb_otg_state_string(musb->xceiv->state));
-               del_timer(&glue->timer);
-               glue->last_timer = jiffies;
-               return;
-       }
-       if (musb->port_mode == MUSB_PORT_MODE_HOST)
-               return;
-
-       if (time_after(glue->last_timer, timeout) &&
-                               timer_pending(&glue->timer)) {
-               dev_dbg(musb->controller,
-                       "Longer idle timer already pending, ignoring...\n");
-               return;
-       }
-       glue->last_timer = timeout;
-
-       dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
-               usb_otg_state_string(musb->xceiv->state),
-                       jiffies_to_msecs(timeout - jiffies));
-       mod_timer(&glue->timer, timeout);
-}
-
 static irqreturn_t dsps_interrupt(int irq, void *hci)
 {
        struct musb  *musb = hci;
@@ -443,7 +445,7 @@ static int get_musb_port_mode(struct device *dev)
        case USB_DR_MODE_OTG:
        default:
                return MUSB_PORT_MODE_DUAL_ROLE;
-       };
+       }
 }
 
 static int dsps_create_musb_pdev(struct dsps_glue *glue,
@@ -631,7 +633,7 @@ static struct platform_driver dsps_usbss_driver = {
        .remove         = dsps_remove,
        .driver         = {
                .name   = "musb-dsps",
-               .of_match_table = of_match_ptr(musb_dsps_of_match),
+               .of_match_table = musb_dsps_of_match,
        },
 };
 
index 3671898a4535b3cf2de3c0e188f4947485d2760d..d2d3a173b31503b54f9071e9af48f4878567856f 100644 (file)
@@ -1121,7 +1121,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
                        case USB_ENDPOINT_XFER_BULK:    s = "bulk"; break;
                        case USB_ENDPOINT_XFER_INT:     s = "int"; break;
                        default:                        s = "iso"; break;
-                       }; s; }),
+                       } s; }),
                        musb_ep->is_in ? "IN" : "OUT",
                        musb_ep->dma ? "dma, " : "",
                        musb_ep->packet_sz);
index 9a2b8c85f19af4ea05bca5c1e90fe08ac345f22b..6582a20bec05db6be2730f6ffee4f3677e6b2340 100644 (file)
@@ -253,7 +253,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
                        case USB_ENDPOINT_XFER_BULK:    s = "-bulk"; break;
                        case USB_ENDPOINT_XFER_ISOC:    s = "-iso"; break;
                        default:                        s = "-intr"; break;
-                       }; s; }),
+                       } s; }),
                        epnum, buf + offset, len);
 
        /* Configure endpoint */
index d1d6b83aabca61df43dffccf57ccbe7687cbe399..9af6bba5eac964bb7195e78815b22bc1958c6c3c 100644 (file)
@@ -220,6 +220,23 @@ int musb_hub_status_data(struct usb_hcd *hcd, char *buf)
        return retval;
 }
 
+static int musb_has_gadget(struct musb *musb)
+{
+       /*
+        * In host-only mode we start a connection right away. In OTG mode
+        * we have to wait until we loaded a gadget. We don't really need a
+        * gadget if we operate as a host but we should not start a session
+        * as a device without a gadget or else we explode.
+        */
+#ifdef CONFIG_USB_MUSB_HOST
+       return 1;
+#else
+       if (musb->port_mode == MUSB_PORT_MODE_HOST)
+               return 1;
+       return musb->g.dev.driver != NULL;
+#endif
+}
+
 int musb_hub_control(
        struct usb_hcd  *hcd,
        u16             typeReq,
@@ -362,7 +379,7 @@ int musb_hub_control(
                         * initialization logic, e.g. for OTG, or change any
                         * logic relating to VBUS power-up.
                         */
-                       if (!hcd->self.is_b_host)
+                       if (!hcd->self.is_b_host && musb_has_gadget(musb))
                                musb_start(musb);
                        break;
                case USB_PORT_FEAT_RESET:
index 59d2245db1c81ce228d7607aa1176e56bbfb9082..2a408cdaf7b2c7102098faac6d29727505f41c91 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/delay.h>
 #include <linux/usb/musb-omap.h>
 #include <linux/usb/omap_control_usb.h>
+#include <linux/of_platform.h>
 
 #include "musb_core.h"
 #include "omap2430.h"
@@ -305,6 +306,9 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
        default:
                dev_dbg(dev, "ID float\n");
        }
+
+       atomic_notifier_call_chain(&musb->xceiv->notifier,
+                       musb->xceiv->last_event, NULL);
 }
 
 
@@ -348,11 +352,21 @@ static int omap2430_musb_init(struct musb *musb)
         * up through ULPI.  TWL4030-family PMICs include one,
         * which needs a driver, drivers aren't always needed.
         */
-       if (dev->parent->of_node)
+       if (dev->parent->of_node) {
+               musb->phy = devm_phy_get(dev->parent, "usb2-phy");
+
+               /* We can't totally remove musb->xceiv as of now because
+                * musb core uses xceiv.state and xceiv.otg. Once we have
+                * a separate state machine to handle otg, these can be moved
+                * out of xceiv and then we can start using the generic PHY
+                * framework
+                */
                musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent,
                    "usb-phy", 0);
-       else
+       } else {
                musb->xceiv = devm_usb_get_phy_dev(dev, 0);
+               musb->phy = devm_phy_get(dev, "usb");
+       }
 
        if (IS_ERR(musb->xceiv)) {
                status = PTR_ERR(musb->xceiv);
@@ -364,6 +378,10 @@ static int omap2430_musb_init(struct musb *musb)
                return -EPROBE_DEFER;
        }
 
+       if (IS_ERR(musb->phy)) {
+               pr_err("HS USB OTG: no PHY configured\n");
+               return PTR_ERR(musb->phy);
+       }
        musb->isr = omap2430_musb_interrupt;
 
        status = pm_runtime_get_sync(dev);
@@ -397,7 +415,7 @@ static int omap2430_musb_init(struct musb *musb)
        if (glue->status != OMAP_MUSB_UNKNOWN)
                omap_musb_set_mailbox(glue);
 
-       usb_phy_init(musb->xceiv);
+       phy_init(musb->phy);
 
        pm_runtime_put_noidle(musb->controller);
        return 0;
@@ -460,6 +478,7 @@ static int omap2430_musb_exit(struct musb *musb)
        del_timer_sync(&musb_idle_timer);
 
        omap2430_low_level_exit(musb);
+       phy_exit(musb->phy);
 
        return 0;
 }
@@ -509,8 +528,12 @@ static int omap2430_probe(struct platform_device *pdev)
        glue->dev                       = &pdev->dev;
        glue->musb                      = musb;
        glue->status                    = OMAP_MUSB_UNKNOWN;
+       glue->control_otghs = ERR_PTR(-ENODEV);
 
        if (np) {
+               struct device_node *control_node;
+               struct platform_device *control_pdev;
+
                pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
                if (!pdata) {
                        dev_err(&pdev->dev,
@@ -539,22 +562,20 @@ static int omap2430_probe(struct platform_device *pdev)
                of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
                of_property_read_u32(np, "power", (u32 *)&pdata->power);
                config->multipoint = of_property_read_bool(np, "multipoint");
-               pdata->has_mailbox = of_property_read_bool(np,
-                   "ti,has-mailbox");
 
                pdata->board_data       = data;
                pdata->config           = config;
-       }
 
-       if (pdata->has_mailbox) {
-               glue->control_otghs = omap_get_control_dev();
-               if (IS_ERR(glue->control_otghs)) {
-                       dev_vdbg(&pdev->dev, "Failed to get control device\n");
-                       ret = PTR_ERR(glue->control_otghs);
-                       goto err2;
+               control_node = of_parse_phandle(np, "ctrl-module", 0);
+               if (control_node) {
+                       control_pdev = of_find_device_by_node(control_node);
+                       if (!control_pdev) {
+                               dev_err(&pdev->dev, "Failed to get control device\n");
+                               ret = -EINVAL;
+                               goto err2;
+                       }
+                       glue->control_otghs = &control_pdev->dev;
                }
-       } else {
-               glue->control_otghs = ERR_PTR(-ENODEV);
        }
        pdata->platform_ops             = &omap2430_ops;
 
@@ -638,7 +659,7 @@ static int omap2430_runtime_suspend(struct device *dev)
                                OTG_INTERFSEL);
 
                omap2430_low_level_exit(musb);
-               usb_phy_set_suspend(musb->xceiv, 1);
+               phy_power_off(musb->phy);
        }
 
        return 0;
@@ -653,8 +674,7 @@ static int omap2430_runtime_resume(struct device *dev)
                omap2430_low_level_init(musb);
                musb_writel(musb->mregs, OTG_INTERFSEL,
                                musb->context.otg_interfsel);
-
-               usb_phy_set_suspend(musb->xceiv, 0);
+               phy_power_on(musb->phy);
        }
 
        return 0;
index b3b3ed723882ffab75829e7c9bc1b08b42e1605d..4432314d70ee18f1dfe0e092c487dcab224c1742 100644 (file)
@@ -1152,7 +1152,11 @@ static const struct musb_platform_ops tusb_ops = {
        .set_vbus       = tusb_musb_set_vbus,
 };
 
-static u64 tusb_dmamask = DMA_BIT_MASK(32);
+static const struct platform_device_info tusb_dev_info = {
+       .name           = "musb-hdrc",
+       .id             = PLATFORM_DEVID_AUTO,
+       .dma_mask       = DMA_BIT_MASK(32),
+};
 
 static int tusb_probe(struct platform_device *pdev)
 {
@@ -1160,7 +1164,7 @@ static int tusb_probe(struct platform_device *pdev)
        struct musb_hdrc_platform_data  *pdata = dev_get_platdata(&pdev->dev);
        struct platform_device          *musb;
        struct tusb6010_glue            *glue;
-
+       struct platform_device_info     pinfo;
        int                             ret = -ENOMEM;
 
        glue = kzalloc(sizeof(*glue), GFP_KERNEL);
@@ -1169,18 +1173,7 @@ static int tusb_probe(struct platform_device *pdev)
                goto err0;
        }
 
-       musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
-       if (!musb) {
-               dev_err(&pdev->dev, "failed to allocate musb device\n");
-               goto err1;
-       }
-
-       musb->dev.parent                = &pdev->dev;
-       musb->dev.dma_mask              = &tusb_dmamask;
-       musb->dev.coherent_dma_mask     = tusb_dmamask;
-
        glue->dev                       = &pdev->dev;
-       glue->musb                      = musb;
 
        pdata->platform_ops             = &tusb_ops;
 
@@ -1204,31 +1197,23 @@ static int tusb_probe(struct platform_device *pdev)
        musb_resources[2].end = pdev->resource[2].end;
        musb_resources[2].flags = pdev->resource[2].flags;
 
-       ret = platform_device_add_resources(musb, musb_resources,
-                       ARRAY_SIZE(musb_resources));
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add resources\n");
-               goto err3;
-       }
-
-       ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add platform_data\n");
-               goto err3;
-       }
-
-       ret = platform_device_add(musb);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register musb device\n");
+       pinfo = tusb_dev_info;
+       pinfo.parent = &pdev->dev;
+       pinfo.res = musb_resources;
+       pinfo.num_res = ARRAY_SIZE(musb_resources);
+       pinfo.data = pdata;
+       pinfo.size_data = sizeof(*pdata);
+
+       glue->musb = musb = platform_device_register_full(&pinfo);
+       if (IS_ERR(musb)) {
+               ret = PTR_ERR(musb);
+               dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
                goto err3;
        }
 
        return 0;
 
 err3:
-       platform_device_put(musb);
-
-err1:
        kfree(glue);
 
 err0:
index 59256b12f7469fe9170ec0fa7db0bee3cbbed6da..f483d1924c282883b6e679d419885884959c33c9 100644 (file)
@@ -376,17 +376,10 @@ static int ux500_resume(struct device *dev)
 
        return 0;
 }
-
-static const struct dev_pm_ops ux500_pm_ops = {
-       .suspend        = ux500_suspend,
-       .resume         = ux500_resume,
-};
-
-#define DEV_PM_OPS     (&ux500_pm_ops)
-#else
-#define DEV_PM_OPS     NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(ux500_pm_ops, ux500_suspend, ux500_resume);
+
 static const struct of_device_id ux500_match[] = {
         { .compatible = "stericsson,db8500-musb", },
         {}
@@ -397,7 +390,7 @@ static struct platform_driver ux500_driver = {
        .remove         = ux500_remove,
        .driver         = {
                .name   = "musb-ux500",
-               .pm     = DEV_PM_OPS,
+               .pm     = &ux500_pm_ops,
                .of_match_table = ux500_match,
        },
 };
index d5589f9c60a92e3c96757c8a6ba1fd6a171e01b1..08e2f39027ec00ae0a7c5ae07b46fdd1cdbfae8f 100644 (file)
@@ -66,17 +66,6 @@ config OMAP_CONTROL_USB
          power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
          additional register to power on USB3 PHY.
 
-config OMAP_USB2
-       tristate "OMAP USB2 PHY Driver"
-       depends on ARCH_OMAP2PLUS
-       select OMAP_CONTROL_USB
-       select USB_PHY
-       help
-         Enable this to support the transceiver that is part of SOC. This
-         driver takes care of all the PHY functionality apart from comparator.
-         The USB OTG controller communicates with the comparator using this
-         driver.
-
 config OMAP_USB3
        tristate "OMAP USB3 PHY Driver"
        depends on ARCH_OMAP2PLUS || COMPILE_TEST
@@ -93,6 +82,7 @@ config AM335X_CONTROL_USB
 
 config AM335X_PHY_USB
        tristate "AM335x USB PHY Driver"
+       depends on ARM || COMPILE_TEST
        select USB_PHY
        select AM335X_CONTROL_USB
        select NOP_USB_XCEIV
@@ -123,16 +113,6 @@ config SAMSUNG_USB3PHY
          Enable this to support Samsung USB 3.0 (Super Speed) phy controller
          for samsung SoCs.
 
-config TWL4030_USB
-       tristate "TWL4030 USB Transceiver Driver"
-       depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
-       select USB_PHY
-       help
-         Enable this to support the USB OTG transceiver on TWL4030
-         family chips (including the TWL5030 and TPS659x0 devices).
-         This transceiver supports high and full speed devices plus,
-         in host mode, low speed.
-
 config TWL6030_USB
        tristate "TWL6030 USB Transceiver Driver"
        depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS
@@ -214,6 +194,19 @@ config USB_RCAR_PHY
          To compile this driver as a module, choose M here: the
          module will be called phy-rcar-usb.
 
+config USB_RCAR_GEN2_PHY
+       tristate "Renesas R-Car Gen2 USB PHY support"
+       depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
+       select USB_PHY
+       help
+         Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver.
+         It is typically used to control internal USB PHY for USBHS,
+         and to configure shared USB channels 0 and 2.
+         This driver supports R8A7790 and R8A7791.
+
+         To compile this driver as a module, choose M here: the
+         module will be called phy-rcar-gen2-usb.
+
 config USB_ULPI
        bool "Generic ULPI Transceiver Driver"
        depends on ARM
index 2135e85f46eda29f104940467154a789db870683..022c1da7fb786ffb8a1228e6fc890d442941e9ae 100644 (file)
@@ -15,12 +15,10 @@ obj-$(CONFIG_NOP_USB_XCEIV)         += phy-generic.o
 obj-$(CONFIG_OMAP_CONTROL_USB)         += phy-omap-control.o
 obj-$(CONFIG_AM335X_CONTROL_USB)       += phy-am335x-control.o
 obj-$(CONFIG_AM335X_PHY_USB)           += phy-am335x.o
-obj-$(CONFIG_OMAP_USB2)                        += phy-omap-usb2.o
 obj-$(CONFIG_OMAP_USB3)                        += phy-omap-usb3.o
 obj-$(CONFIG_SAMSUNG_USBPHY)           += phy-samsung-usb.o
 obj-$(CONFIG_SAMSUNG_USB2PHY)          += phy-samsung-usb2.o
 obj-$(CONFIG_SAMSUNG_USB3PHY)          += phy-samsung-usb3.o
-obj-$(CONFIG_TWL4030_USB)              += phy-twl4030-usb.o
 obj-$(CONFIG_TWL6030_USB)              += phy-twl6030-usb.o
 obj-$(CONFIG_USB_EHCI_TEGRA)           += phy-tegra-usb.o
 obj-$(CONFIG_USB_GPIO_VBUS)            += phy-gpio-vbus-usb.o
@@ -29,5 +27,6 @@ obj-$(CONFIG_USB_MSM_OTG)             += phy-msm-usb.o
 obj-$(CONFIG_USB_MV_OTG)               += phy-mv-usb.o
 obj-$(CONFIG_USB_MXS_PHY)              += phy-mxs-usb.o
 obj-$(CONFIG_USB_RCAR_PHY)             += phy-rcar-usb.o
+obj-$(CONFIG_USB_RCAR_GEN2_PHY)                += phy-rcar-gen2-usb.o
 obj-$(CONFIG_USB_ULPI)                 += phy-ulpi.o
 obj-$(CONFIG_USB_ULPI_VIEWPORT)                += phy-ulpi-viewport.o
index 22cf07d62e4c68f04378b18a70555eb782ba25ff..634f49acd20e58154d02177c4e49781e51754c8d 100644 (file)
@@ -26,6 +26,41 @@ struct am335x_control_usb {
 #define USBPHY_OTGVDET_EN      (1 << 19)
 #define USBPHY_OTGSESSEND_EN   (1 << 20)
 
+#define AM335X_PHY0_WK_EN      (1 << 0)
+#define AM335X_PHY1_WK_EN      (1 << 8)
+
+static void am335x_phy_wkup(struct  phy_control *phy_ctrl, u32 id, bool on)
+{
+       struct am335x_control_usb *usb_ctrl;
+       u32 val;
+       u32 reg;
+
+       usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl);
+
+       switch (id) {
+       case 0:
+               reg = AM335X_PHY0_WK_EN;
+               break;
+       case 1:
+               reg = AM335X_PHY1_WK_EN;
+               break;
+       default:
+               WARN_ON(1);
+               return;
+       }
+
+       spin_lock(&usb_ctrl->lock);
+       val = readl(usb_ctrl->wkup);
+
+       if (on)
+               val |= reg;
+       else
+               val &= ~reg;
+
+       writel(val, usb_ctrl->wkup);
+       spin_unlock(&usb_ctrl->lock);
+}
+
 static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on)
 {
        struct am335x_control_usb *usb_ctrl;
@@ -59,6 +94,7 @@ static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on)
 
 static const struct phy_control ctrl_am335x = {
        .phy_power = am335x_phy_power,
+       .phy_wkup = am335x_phy_wkup,
 };
 
 static const struct of_device_id omap_control_usb_id_table[] = {
@@ -117,6 +153,12 @@ static int am335x_control_usb_probe(struct platform_device *pdev)
        ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(ctrl_usb->phy_reg))
                return PTR_ERR(ctrl_usb->phy_reg);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wakeup");
+       ctrl_usb->wkup = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ctrl_usb->wkup))
+               return PTR_ERR(ctrl_usb->wkup);
+
        spin_lock_init(&ctrl_usb->lock);
        ctrl_usb->phy_ctrl = *phy_ctrl;
 
@@ -129,7 +171,7 @@ static struct platform_driver am335x_control_driver = {
        .driver         = {
                .name   = "am335x-control-usb",
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(omap_control_usb_id_table),
+               .of_match_table = omap_control_usb_id_table,
        },
 };
 
index c4d614d1f17349972d3f629a45949c2e2addef9f..6370e50649d7f732c640fd65879c26d82ec5f994 100644 (file)
@@ -53,21 +53,20 @@ static int am335x_phy_probe(struct platform_device *pdev)
        }
 
        ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen,
-                       USB_PHY_TYPE_USB2, 0, false, false);
+                       USB_PHY_TYPE_USB2, 0, false);
        if (ret)
                return ret;
 
        ret = usb_add_phy_dev(&am_phy->usb_phy_gen.phy);
        if (ret)
-               goto err_add;
+               return ret;
        am_phy->usb_phy_gen.phy.init = am335x_init;
        am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown;
 
        platform_set_drvdata(pdev, am_phy);
+
        return 0;
 
-err_add:
-       usb_phy_gen_cleanup_phy(&am_phy->usb_phy_gen);
        return ret;
 }
 
@@ -79,6 +78,40 @@ static int am335x_phy_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int am335x_phy_runtime_suspend(struct device *dev)
+{
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct am335x_phy *am_phy = platform_get_drvdata(pdev);
+
+       if (device_may_wakeup(dev))
+               phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true);
+       phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
+       return 0;
+}
+
+static int am335x_phy_runtime_resume(struct device *dev)
+{
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct am335x_phy       *am_phy = platform_get_drvdata(pdev);
+
+       phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true);
+       if (device_may_wakeup(dev))
+               phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false);
+       return 0;
+}
+
+static const struct dev_pm_ops am335x_pm_ops = {
+       SET_RUNTIME_PM_OPS(am335x_phy_runtime_suspend,
+                       am335x_phy_runtime_resume, NULL)
+};
+
+#define DEV_PM_OPS     (&am335x_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
 static const struct of_device_id am335x_phy_ids[] = {
        { .compatible = "ti,am335x-usb-phy" },
        { }
@@ -91,7 +124,8 @@ static struct platform_driver am335x_phy_driver = {
        .driver         = {
                .name   = "am335x-phy-driver",
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(am335x_phy_ids),
+               .pm = DEV_PM_OPS,
+               .of_match_table = am335x_phy_ids,
        },
 };
 
index fa7c9f9628b599e072b7062ff03270c35714161f..7f3c73b967ce6faf804ee1f29b82acbdde1f59f1 100644 (file)
@@ -134,7 +134,7 @@ int write_ulpi(u8 addr, u8 data)
 /* Operations that will be called from OTG Finite State Machine */
 
 /* Charge vbus for vbus pulsing in SRP */
-void fsl_otg_chrg_vbus(int on)
+void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
 {
        u32 tmp;
 
@@ -170,7 +170,7 @@ void fsl_otg_dischrg_vbus(int on)
 }
 
 /* A-device driver vbus, controlled through PP bit in PORTSC */
-void fsl_otg_drv_vbus(int on)
+void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
 {
        u32 tmp;
 
@@ -188,7 +188,7 @@ void fsl_otg_drv_vbus(int on)
  * Pull-up D+, signalling connect by periperal. Also used in
  * data-line pulsing in SRP
  */
-void fsl_otg_loc_conn(int on)
+void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
 {
        u32 tmp;
 
@@ -207,7 +207,7 @@ void fsl_otg_loc_conn(int on)
  * port.  In host mode, controller will automatically send SOF.
  * Suspend will block the data on the port.
  */
-void fsl_otg_loc_sof(int on)
+void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
 {
        u32 tmp;
 
@@ -222,7 +222,7 @@ void fsl_otg_loc_sof(int on)
 }
 
 /* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
-void fsl_otg_start_pulse(void)
+void fsl_otg_start_pulse(struct otg_fsm *fsm)
 {
        u32 tmp;
 
@@ -235,7 +235,7 @@ void fsl_otg_start_pulse(void)
        fsl_otg_loc_conn(1);
 #endif
 
-       fsl_otg_add_timer(b_data_pulse_tmr);
+       fsl_otg_add_timer(fsm, b_data_pulse_tmr);
 }
 
 void b_data_pulse_end(unsigned long foo)
@@ -252,14 +252,14 @@ void b_data_pulse_end(unsigned long foo)
 void fsl_otg_pulse_vbus(void)
 {
        srp_wait_done = 0;
-       fsl_otg_chrg_vbus(1);
+       fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1);
        /* start the timer to end vbus charge */
-       fsl_otg_add_timer(b_vbus_pulse_tmr);
+       fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr);
 }
 
 void b_vbus_pulse_end(unsigned long foo)
 {
-       fsl_otg_chrg_vbus(0);
+       fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0);
 
        /*
         * As USB3300 using the same a_sess_vld and b_sess_vld voltage
@@ -267,7 +267,7 @@ void b_vbus_pulse_end(unsigned long foo)
         * residual voltage of vbus pulsing and A device pull up
         */
        fsl_otg_dischrg_vbus(1);
-       fsl_otg_add_timer(b_srp_wait_tmr);
+       fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr);
 }
 
 void b_srp_end(unsigned long foo)
@@ -289,7 +289,7 @@ void a_wait_enum(unsigned long foo)
 {
        VDBG("a_wait_enum timeout\n");
        if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
-               fsl_otg_add_timer(a_wait_enum_tmr);
+               fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr);
        else
                otg_statemachine(&fsl_otg_dev->fsm);
 }
@@ -375,8 +375,42 @@ void fsl_otg_uninit_timers(void)
        kfree(b_vbus_pulse_tmr);
 }
 
+static struct fsl_otg_timer *fsl_otg_get_timer(enum otg_fsm_timer t)
+{
+       struct fsl_otg_timer *timer;
+
+       /* REVISIT: use array of pointers to timers instead */
+       switch (t) {
+       case A_WAIT_VRISE:
+               timer = a_wait_vrise_tmr;
+               break;
+       case A_WAIT_BCON:
+               timer = a_wait_vrise_tmr;
+               break;
+       case A_AIDL_BDIS:
+               timer = a_wait_vrise_tmr;
+               break;
+       case B_ASE0_BRST:
+               timer = a_wait_vrise_tmr;
+               break;
+       case B_SE0_SRP:
+               timer = a_wait_vrise_tmr;
+               break;
+       case B_SRP_FAIL:
+               timer = a_wait_vrise_tmr;
+               break;
+       case A_WAIT_ENUM:
+               timer = a_wait_vrise_tmr;
+               break;
+       default:
+               timer = NULL;
+       }
+
+       return timer;
+}
+
 /* Add timer to timer list */
-void fsl_otg_add_timer(void *gtimer)
+void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer)
 {
        struct fsl_otg_timer *timer = gtimer;
        struct fsl_otg_timer *tmp_timer;
@@ -394,8 +428,19 @@ void fsl_otg_add_timer(void *gtimer)
        list_add_tail(&timer->list, &active_timers);
 }
 
+static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+{
+       struct fsl_otg_timer *timer;
+
+       timer = fsl_otg_get_timer(t);
+       if (!timer)
+               return;
+
+       fsl_otg_add_timer(fsm, timer);
+}
+
 /* Remove timer from the timer list; clear timeout status */
-void fsl_otg_del_timer(void *gtimer)
+void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer)
 {
        struct fsl_otg_timer *timer = gtimer;
        struct fsl_otg_timer *tmp_timer, *del_tmp;
@@ -405,6 +450,17 @@ void fsl_otg_del_timer(void *gtimer)
                        list_del(&timer->list);
 }
 
+static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+{
+       struct fsl_otg_timer *timer;
+
+       timer = fsl_otg_get_timer(t);
+       if (!timer)
+               return;
+
+       fsl_otg_del_timer(fsm, timer);
+}
+
 /*
  * Reduce timer count by 1, and find timeout conditions.
  * Called by fsl_otg 1ms timer interrupt
@@ -468,7 +524,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
                                retval = dev->driver->pm->resume(dev);
                                if (fsm->id) {
                                        /* default-b */
-                                       fsl_otg_drv_vbus(1);
+                                       fsl_otg_drv_vbus(fsm, 1);
                                        /*
                                         * Workaround: b_host can't driver
                                         * vbus, but PP in PORTSC needs to
@@ -493,7 +549,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
                                        retval = dev->driver->pm->suspend(dev);
                                if (fsm->id)
                                        /* default-b */
-                                       fsl_otg_drv_vbus(0);
+                                       fsl_otg_drv_vbus(fsm, 0);
                        }
                        otg_dev->host_working = 0;
                }
@@ -757,8 +813,8 @@ static struct otg_fsm_ops fsl_otg_ops = {
        .loc_sof = fsl_otg_loc_sof,
        .start_pulse = fsl_otg_start_pulse,
 
-       .add_timer = fsl_otg_add_timer,
-       .del_timer = fsl_otg_del_timer,
+       .add_timer = fsl_otg_fsm_add_timer,
+       .del_timer = fsl_otg_fsm_del_timer,
 
        .start_host = fsl_otg_start_host,
        .start_gadget = fsl_otg_start_gadget,
@@ -1011,7 +1067,7 @@ static int show_fsl_usb2_otg_state(struct device *dev,
                        "b_bus_suspend: %d\n"
                        "b_conn: %d\n"
                        "b_se0_srp: %d\n"
-                       "b_sess_end: %d\n"
+                       "b_ssend_srp: %d\n"
                        "b_sess_vld: %d\n"
                        "id: %d\n",
                        fsm->a_bus_req,
@@ -1026,7 +1082,7 @@ static int show_fsl_usb2_otg_state(struct device *dev,
                        fsm->b_bus_suspend,
                        fsm->b_conn,
                        fsm->b_se0_srp,
-                       fsm->b_sess_end,
+                       fsm->b_ssend_srp,
                        fsm->b_sess_vld,
                        fsm->id);
        size -= t;
@@ -1057,7 +1113,7 @@ static long fsl_otg_ioctl(struct file *file, unsigned int cmd,
                break;
 
        case SET_A_SUSPEND_REQ:
-               fsl_otg_dev->fsm.a_suspend_req = arg;
+               fsl_otg_dev->fsm.a_suspend_req_inf = arg;
                break;
 
        case SET_A_BUS_DROP:
index e1859b8ef5679dc5c39a367c7e86979a2cfde5ec..7365170a2f23eb482693762edd1aceac034c1702 100644 (file)
@@ -401,6 +401,6 @@ struct fsl_otg_config {
 #define GET_A_BUS_REQ          _IOR(OTG_IOCTL_MAGIC, 8, int)
 #define GET_B_BUS_REQ          _IOR(OTG_IOCTL_MAGIC, 9, int)
 
-void fsl_otg_add_timer(void *timer);
-void fsl_otg_del_timer(void *timer);
+void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer);
+void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer);
 void fsl_otg_pulse_vbus(void);
index 7f4596606e18b41565ef338598298b6f36c64269..329c2d2f85959963c7828d3d9332315062480623 100644 (file)
@@ -41,17 +41,17 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
                        fsm->protocol, protocol);
                /* stop old protocol */
                if (fsm->protocol == PROTO_HOST)
-                       ret = fsm->ops->start_host(fsm, 0);
+                       ret = otg_start_host(fsm, 0);
                else if (fsm->protocol == PROTO_GADGET)
-                       ret = fsm->ops->start_gadget(fsm, 0);
+                       ret = otg_start_gadget(fsm, 0);
                if (ret)
                        return ret;
 
                /* start new protocol */
                if (protocol == PROTO_HOST)
-                       ret = fsm->ops->start_host(fsm, 1);
+                       ret = otg_start_host(fsm, 1);
                else if (protocol == PROTO_GADGET)
-                       ret = fsm->ops->start_gadget(fsm, 1);
+                       ret = otg_start_gadget(fsm, 1);
                if (ret)
                        return ret;
 
@@ -69,42 +69,50 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
 {
        switch (old_state) {
        case OTG_STATE_B_IDLE:
-               otg_del_timer(fsm, b_se0_srp_tmr);
+               otg_del_timer(fsm, B_SE0_SRP);
                fsm->b_se0_srp = 0;
+               fsm->adp_sns = 0;
+               fsm->adp_prb = 0;
                break;
        case OTG_STATE_B_SRP_INIT:
+               fsm->data_pulse = 0;
                fsm->b_srp_done = 0;
                break;
        case OTG_STATE_B_PERIPHERAL:
                break;
        case OTG_STATE_B_WAIT_ACON:
-               otg_del_timer(fsm, b_ase0_brst_tmr);
+               otg_del_timer(fsm, B_ASE0_BRST);
                fsm->b_ase0_brst_tmout = 0;
                break;
        case OTG_STATE_B_HOST:
                break;
        case OTG_STATE_A_IDLE:
+               fsm->adp_prb = 0;
                break;
        case OTG_STATE_A_WAIT_VRISE:
-               otg_del_timer(fsm, a_wait_vrise_tmr);
+               otg_del_timer(fsm, A_WAIT_VRISE);
                fsm->a_wait_vrise_tmout = 0;
                break;
        case OTG_STATE_A_WAIT_BCON:
-               otg_del_timer(fsm, a_wait_bcon_tmr);
+               otg_del_timer(fsm, A_WAIT_BCON);
                fsm->a_wait_bcon_tmout = 0;
                break;
        case OTG_STATE_A_HOST:
-               otg_del_timer(fsm, a_wait_enum_tmr);
+               otg_del_timer(fsm, A_WAIT_ENUM);
                break;
        case OTG_STATE_A_SUSPEND:
-               otg_del_timer(fsm, a_aidl_bdis_tmr);
+               otg_del_timer(fsm, A_AIDL_BDIS);
                fsm->a_aidl_bdis_tmout = 0;
-               fsm->a_suspend_req = 0;
+               fsm->a_suspend_req_inf = 0;
                break;
        case OTG_STATE_A_PERIPHERAL:
+               otg_del_timer(fsm, A_BIDL_ADIS);
+               fsm->a_bidl_adis_tmout = 0;
                break;
        case OTG_STATE_A_WAIT_VFALL:
-               otg_del_timer(fsm, a_wait_vrise_tmr);
+               otg_del_timer(fsm, A_WAIT_VFALL);
+               fsm->a_wait_vfall_tmout = 0;
+               otg_del_timer(fsm, A_WAIT_VRISE);
                break;
        case OTG_STATE_A_VBUS_ERR:
                break;
@@ -127,14 +135,19 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
                otg_chrg_vbus(fsm, 0);
                otg_loc_conn(fsm, 0);
                otg_loc_sof(fsm, 0);
+               /*
+                * Driver is responsible for starting ADP probing
+                * if ADP sensing times out.
+                */
+               otg_start_adp_sns(fsm);
                otg_set_protocol(fsm, PROTO_UNDEF);
-               otg_add_timer(fsm, b_se0_srp_tmr);
+               otg_add_timer(fsm, B_SE0_SRP);
                break;
        case OTG_STATE_B_SRP_INIT:
                otg_start_pulse(fsm);
                otg_loc_sof(fsm, 0);
                otg_set_protocol(fsm, PROTO_UNDEF);
-               otg_add_timer(fsm, b_srp_fail_tmr);
+               otg_add_timer(fsm, B_SRP_FAIL);
                break;
        case OTG_STATE_B_PERIPHERAL:
                otg_chrg_vbus(fsm, 0);
@@ -147,7 +160,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
                otg_loc_conn(fsm, 0);
                otg_loc_sof(fsm, 0);
                otg_set_protocol(fsm, PROTO_HOST);
-               otg_add_timer(fsm, b_ase0_brst_tmr);
+               otg_add_timer(fsm, B_ASE0_BRST);
                fsm->a_bus_suspend = 0;
                break;
        case OTG_STATE_B_HOST:
@@ -163,6 +176,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
                otg_chrg_vbus(fsm, 0);
                otg_loc_conn(fsm, 0);
                otg_loc_sof(fsm, 0);
+               otg_start_adp_prb(fsm);
                otg_set_protocol(fsm, PROTO_HOST);
                break;
        case OTG_STATE_A_WAIT_VRISE:
@@ -170,14 +184,14 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
                otg_loc_conn(fsm, 0);
                otg_loc_sof(fsm, 0);
                otg_set_protocol(fsm, PROTO_HOST);
-               otg_add_timer(fsm, a_wait_vrise_tmr);
+               otg_add_timer(fsm, A_WAIT_VRISE);
                break;
        case OTG_STATE_A_WAIT_BCON:
                otg_drv_vbus(fsm, 1);
                otg_loc_conn(fsm, 0);
                otg_loc_sof(fsm, 0);
                otg_set_protocol(fsm, PROTO_HOST);
-               otg_add_timer(fsm, a_wait_bcon_tmr);
+               otg_add_timer(fsm, A_WAIT_BCON);
                break;
        case OTG_STATE_A_HOST:
                otg_drv_vbus(fsm, 1);
@@ -188,15 +202,15 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
                 * When HNP is triggered while a_bus_req = 0, a_host will
                 * suspend too fast to complete a_set_b_hnp_en
                 */
-               if (!fsm->a_bus_req || fsm->a_suspend_req)
-                       otg_add_timer(fsm, a_wait_enum_tmr);
+               if (!fsm->a_bus_req || fsm->a_suspend_req_inf)
+                       otg_add_timer(fsm, A_WAIT_ENUM);
                break;
        case OTG_STATE_A_SUSPEND:
                otg_drv_vbus(fsm, 1);
                otg_loc_conn(fsm, 0);
                otg_loc_sof(fsm, 0);
                otg_set_protocol(fsm, PROTO_HOST);
-               otg_add_timer(fsm, a_aidl_bdis_tmr);
+               otg_add_timer(fsm, A_AIDL_BDIS);
 
                break;
        case OTG_STATE_A_PERIPHERAL:
@@ -204,12 +218,14 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
                otg_loc_sof(fsm, 0);
                otg_set_protocol(fsm, PROTO_GADGET);
                otg_drv_vbus(fsm, 1);
+               otg_add_timer(fsm, A_BIDL_ADIS);
                break;
        case OTG_STATE_A_WAIT_VFALL:
                otg_drv_vbus(fsm, 0);
                otg_loc_conn(fsm, 0);
                otg_loc_sof(fsm, 0);
                otg_set_protocol(fsm, PROTO_HOST);
+               otg_add_timer(fsm, A_WAIT_VFALL);
                break;
        case OTG_STATE_A_VBUS_ERR:
                otg_drv_vbus(fsm, 0);
@@ -250,7 +266,8 @@ int otg_statemachine(struct otg_fsm *fsm)
                        otg_set_state(fsm, OTG_STATE_A_IDLE);
                else if (fsm->b_sess_vld && fsm->otg->gadget)
                        otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
-               else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp)
+               else if ((fsm->b_bus_req || fsm->adp_change || fsm->power_up) &&
+                               fsm->b_ssend_srp && fsm->b_se0_srp)
                        otg_set_state(fsm, OTG_STATE_B_SRP_INIT);
                break;
        case OTG_STATE_B_SRP_INIT:
@@ -277,13 +294,14 @@ int otg_statemachine(struct otg_fsm *fsm)
        case OTG_STATE_B_HOST:
                if (!fsm->id || !fsm->b_sess_vld)
                        otg_set_state(fsm, OTG_STATE_B_IDLE);
-               else if (!fsm->b_bus_req || !fsm->a_conn)
+               else if (!fsm->b_bus_req || !fsm->a_conn || fsm->test_device)
                        otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
                break;
        case OTG_STATE_A_IDLE:
                if (fsm->id)
                        otg_set_state(fsm, OTG_STATE_B_IDLE);
-               else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det))
+               else if (!fsm->a_bus_drop && (fsm->a_bus_req ||
+                         fsm->a_srp_det || fsm->adp_change || fsm->power_up))
                        otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE);
                break;
        case OTG_STATE_A_WAIT_VRISE:
@@ -301,7 +319,7 @@ int otg_statemachine(struct otg_fsm *fsm)
                        otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
                break;
        case OTG_STATE_A_HOST:
-               if ((!fsm->a_bus_req || fsm->a_suspend_req) &&
+               if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
                                fsm->otg->host->b_hnp_enable)
                        otg_set_state(fsm, OTG_STATE_A_SUSPEND);
                else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop)
@@ -324,14 +342,14 @@ int otg_statemachine(struct otg_fsm *fsm)
        case OTG_STATE_A_PERIPHERAL:
                if (fsm->id || fsm->a_bus_drop)
                        otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
-               else if (fsm->b_bus_suspend)
+               else if (fsm->a_bidl_adis_tmout || fsm->b_bus_suspend)
                        otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
                else if (!fsm->a_vbus_vld)
                        otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
                break;
        case OTG_STATE_A_WAIT_VFALL:
-               if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld &&
-                                       !fsm->b_conn))
+               if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req ||
+                               (!fsm->a_sess_vld && !fsm->b_conn))
                        otg_set_state(fsm, OTG_STATE_A_IDLE);
                break;
        case OTG_STATE_A_VBUS_ERR:
index fbe586206f33be707bf1bba33340d80a3f0f4437..7441b46a27f1b86ac609b2e6e5c71b8e187cfd1d 100644 (file)
 #define PROTO_HOST     (1)
 #define PROTO_GADGET   (2)
 
+enum otg_fsm_timer {
+       /* Standard OTG timers */
+       A_WAIT_VRISE,
+       A_WAIT_VFALL,
+       A_WAIT_BCON,
+       A_AIDL_BDIS,
+       B_ASE0_BRST,
+       A_BIDL_ADIS,
+
+       /* Auxiliary timers */
+       B_SE0_SRP,
+       B_SRP_FAIL,
+       A_WAIT_ENUM,
+
+       NUM_OTG_FSM_TIMERS,
+};
+
 /* OTG state machine according to the OTG spec */
 struct otg_fsm {
        /* Input */
+       int id;
+       int adp_change;
+       int power_up;
+       int test_device;
+       int a_bus_drop;
+       int a_bus_req;
+       int a_srp_det;
+       int a_vbus_vld;
+       int b_conn;
        int a_bus_resume;
        int a_bus_suspend;
        int a_conn;
+       int b_bus_req;
+       int b_se0_srp;
+       int b_ssend_srp;
+       int b_sess_vld;
+       /* Auxilary inputs */
        int a_sess_vld;
-       int a_srp_det;
-       int a_vbus_vld;
        int b_bus_resume;
        int b_bus_suspend;
-       int b_conn;
-       int b_se0_srp;
-       int b_sess_end;
-       int b_sess_vld;
-       int id;
+
+       /* Output */
+       int data_pulse;
+       int drv_vbus;
+       int loc_conn;
+       int loc_sof;
+       int adp_prb;
+       int adp_sns;
 
        /* Internal variables */
        int a_set_b_hnp_en;
        int b_srp_done;
        int b_hnp_enable;
+       int a_clr_err;
+
+       /* Informative variables */
+       int a_bus_drop_inf;
+       int a_bus_req_inf;
+       int a_clr_err_inf;
+       int b_bus_req_inf;
+       /* Auxilary informative variables */
+       int a_suspend_req_inf;
 
        /* Timeout indicator for timers */
        int a_wait_vrise_tmout;
+       int a_wait_vfall_tmout;
        int a_wait_bcon_tmout;
        int a_aidl_bdis_tmout;
        int b_ase0_brst_tmout;
-
-       /* Informative variables */
-       int a_bus_drop;
-       int a_bus_req;
-       int a_clr_err;
-       int a_suspend_req;
-       int b_bus_req;
-
-       /* Output */
-       int drv_vbus;
-       int loc_conn;
-       int loc_sof;
+       int a_bidl_adis_tmout;
 
        struct otg_fsm_ops *ops;
        struct usb_otg *otg;
@@ -83,65 +114,123 @@ struct otg_fsm {
 };
 
 struct otg_fsm_ops {
-       void    (*chrg_vbus)(int on);
-       void    (*drv_vbus)(int on);
-       void    (*loc_conn)(int on);
-       void    (*loc_sof)(int on);
-       void    (*start_pulse)(void);
-       void    (*add_timer)(void *timer);
-       void    (*del_timer)(void *timer);
+       void    (*chrg_vbus)(struct otg_fsm *fsm, int on);
+       void    (*drv_vbus)(struct otg_fsm *fsm, int on);
+       void    (*loc_conn)(struct otg_fsm *fsm, int on);
+       void    (*loc_sof)(struct otg_fsm *fsm, int on);
+       void    (*start_pulse)(struct otg_fsm *fsm);
+       void    (*start_adp_prb)(struct otg_fsm *fsm);
+       void    (*start_adp_sns)(struct otg_fsm *fsm);
+       void    (*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
+       void    (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
        int     (*start_host)(struct otg_fsm *fsm, int on);
        int     (*start_gadget)(struct otg_fsm *fsm, int on);
 };
 
 
-static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on)
+static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on)
 {
-       fsm->ops->chrg_vbus(on);
+       if (!fsm->ops->chrg_vbus)
+               return -EOPNOTSUPP;
+       fsm->ops->chrg_vbus(fsm, on);
+       return 0;
 }
 
-static inline void otg_drv_vbus(struct otg_fsm *fsm, int on)
+static inline int otg_drv_vbus(struct otg_fsm *fsm, int on)
 {
+       if (!fsm->ops->drv_vbus)
+               return -EOPNOTSUPP;
        if (fsm->drv_vbus != on) {
                fsm->drv_vbus = on;
-               fsm->ops->drv_vbus(on);
+               fsm->ops->drv_vbus(fsm, on);
        }
+       return 0;
 }
 
-static inline void otg_loc_conn(struct otg_fsm *fsm, int on)
+static inline int otg_loc_conn(struct otg_fsm *fsm, int on)
 {
+       if (!fsm->ops->loc_conn)
+               return -EOPNOTSUPP;
        if (fsm->loc_conn != on) {
                fsm->loc_conn = on;
-               fsm->ops->loc_conn(on);
+               fsm->ops->loc_conn(fsm, on);
        }
+       return 0;
 }
 
-static inline void otg_loc_sof(struct otg_fsm *fsm, int on)
+static inline int otg_loc_sof(struct otg_fsm *fsm, int on)
 {
+       if (!fsm->ops->loc_sof)
+               return -EOPNOTSUPP;
        if (fsm->loc_sof != on) {
                fsm->loc_sof = on;
-               fsm->ops->loc_sof(on);
+               fsm->ops->loc_sof(fsm, on);
+       }
+       return 0;
+}
+
+static inline int otg_start_pulse(struct otg_fsm *fsm)
+{
+       if (!fsm->ops->start_pulse)
+               return -EOPNOTSUPP;
+       if (!fsm->data_pulse) {
+               fsm->data_pulse = 1;
+               fsm->ops->start_pulse(fsm);
        }
+       return 0;
 }
 
-static inline void otg_start_pulse(struct otg_fsm *fsm)
+static inline int otg_start_adp_prb(struct otg_fsm *fsm)
 {
-       fsm->ops->start_pulse();
+       if (!fsm->ops->start_adp_prb)
+               return -EOPNOTSUPP;
+       if (!fsm->adp_prb) {
+               fsm->adp_sns = 0;
+               fsm->adp_prb = 1;
+               fsm->ops->start_adp_prb(fsm);
+       }
+       return 0;
 }
 
-static inline void otg_add_timer(struct otg_fsm *fsm, void *timer)
+static inline int otg_start_adp_sns(struct otg_fsm *fsm)
 {
-       fsm->ops->add_timer(timer);
+       if (!fsm->ops->start_adp_sns)
+               return -EOPNOTSUPP;
+       if (!fsm->adp_sns) {
+               fsm->adp_sns = 1;
+               fsm->ops->start_adp_sns(fsm);
+       }
+       return 0;
 }
 
-static inline void otg_del_timer(struct otg_fsm *fsm, void *timer)
+static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer)
 {
-       fsm->ops->del_timer(timer);
+       if (!fsm->ops->add_timer)
+               return -EOPNOTSUPP;
+       fsm->ops->add_timer(fsm, timer);
+       return 0;
 }
 
-int otg_statemachine(struct otg_fsm *fsm);
+static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer)
+{
+       if (!fsm->ops->del_timer)
+               return -EOPNOTSUPP;
+       fsm->ops->del_timer(fsm, timer);
+       return 0;
+}
 
-/* Defined by device specific driver, for different timer implementation */
-extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr,
-       *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr,
-       *a_wait_enum_tmr;
+static inline int otg_start_host(struct otg_fsm *fsm, int on)
+{
+       if (!fsm->ops->start_host)
+               return -EOPNOTSUPP;
+       return fsm->ops->start_host(fsm, on);
+}
+
+static inline int otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+       if (!fsm->ops->start_gadget)
+               return -EOPNOTSUPP;
+       return fsm->ops->start_gadget(fsm, on);
+}
+
+int otg_statemachine(struct otg_fsm *fsm);
index efe59f3f7fdae946f1d6b5c76b9e6f21a0143be1..fce3a9e9bb5d282ff6b1a64492ad6a55c90e654f 100644 (file)
@@ -35,6 +35,9 @@
 #include <linux/clk.h>
 #include <linux/regulator/consumer.h>
 #include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
 
 #include "phy-generic.h"
 
@@ -64,6 +67,23 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
        return 0;
 }
 
+static void nop_reset_set(struct usb_phy_gen_xceiv *nop, int asserted)
+{
+       int value;
+
+       if (!gpio_is_valid(nop->gpio_reset))
+               return;
+
+       value = asserted;
+       if (nop->reset_active_low)
+               value = !value;
+
+       gpio_set_value_cansleep(nop->gpio_reset, value);
+
+       if (!asserted)
+               usleep_range(10000, 20000);
+}
+
 int usb_gen_phy_init(struct usb_phy *phy)
 {
        struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev);
@@ -74,13 +94,10 @@ int usb_gen_phy_init(struct usb_phy *phy)
        }
 
        if (!IS_ERR(nop->clk))
-               clk_enable(nop->clk);
+               clk_prepare_enable(nop->clk);
 
-       if (!IS_ERR(nop->reset)) {
-               /* De-assert RESET */
-               if (regulator_enable(nop->reset))
-                       dev_err(phy->dev, "Failed to de-assert reset\n");
-       }
+       /* De-assert RESET */
+       nop_reset_set(nop, 0);
 
        return 0;
 }
@@ -90,14 +107,11 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
 {
        struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev);
 
-       if (!IS_ERR(nop->reset)) {
-               /* Assert RESET */
-               if (regulator_disable(nop->reset))
-                       dev_err(phy->dev, "Failed to assert reset\n");
-       }
+       /* Assert RESET */
+       nop_reset_set(nop, 1);
 
        if (!IS_ERR(nop->clk))
-               clk_disable(nop->clk);
+               clk_disable_unprepare(nop->clk);
 
        if (!IS_ERR(nop->vcc)) {
                if (regulator_disable(nop->vcc))
@@ -136,8 +150,7 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
 }
 
 int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
-               enum usb_phy_type type, u32 clk_rate, bool needs_vcc,
-               bool needs_reset)
+               enum usb_phy_type type, u32 clk_rate, bool needs_vcc)
 {
        int err;
 
@@ -160,14 +173,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
                }
        }
 
-       if (!IS_ERR(nop->clk)) {
-               err = clk_prepare(nop->clk);
-               if (err) {
-                       dev_err(dev, "Error preparing clock\n");
-                       return err;
-               }
-       }
-
        nop->vcc = devm_regulator_get(dev, "vcc");
        if (IS_ERR(nop->vcc)) {
                dev_dbg(dev, "Error getting vcc regulator: %ld\n",
@@ -176,12 +181,22 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
                        return -EPROBE_DEFER;
        }
 
-       nop->reset = devm_regulator_get(dev, "reset");
-       if (IS_ERR(nop->reset)) {
-               dev_dbg(dev, "Error getting reset regulator: %ld\n",
-                                       PTR_ERR(nop->reset));
-               if (needs_reset)
-                       return -EPROBE_DEFER;
+       if (gpio_is_valid(nop->gpio_reset)) {
+               unsigned long gpio_flags;
+
+               /* Assert RESET */
+               if (nop->reset_active_low)
+                       gpio_flags = GPIOF_OUT_INIT_LOW;
+               else
+                       gpio_flags = GPIOF_OUT_INIT_HIGH;
+
+               err = devm_gpio_request_one(dev, nop->gpio_reset,
+                                               gpio_flags, dev_name(dev));
+               if (err) {
+                       dev_err(dev, "Error requesting RESET GPIO %d\n",
+                                       nop->gpio_reset);
+                       return err;
+               }
        }
 
        nop->dev                = dev;
@@ -200,13 +215,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
 }
 EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
 
-void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop)
-{
-       if (!IS_ERR(nop->clk))
-               clk_unprepare(nop->clk);
-}
-EXPORT_SYMBOL_GPL(usb_phy_gen_cleanup_phy);
-
 static int usb_phy_gen_xceiv_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -217,31 +225,36 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev)
        int err;
        u32 clk_rate = 0;
        bool needs_vcc = false;
-       bool needs_reset = false;
+
+       nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
+       if (!nop)
+               return -ENOMEM;
+
+       nop->reset_active_low = true;   /* default behaviour */
 
        if (dev->of_node) {
                struct device_node *node = dev->of_node;
+               enum of_gpio_flags flags;
 
                if (of_property_read_u32(node, "clock-frequency", &clk_rate))
                        clk_rate = 0;
 
                needs_vcc = of_property_read_bool(node, "vcc-supply");
-               needs_reset = of_property_read_bool(node, "reset-supply");
+               nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios",
+                                                               0, &flags);
+               if (nop->gpio_reset == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+
+               nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
 
        } else if (pdata) {
                type = pdata->type;
                clk_rate = pdata->clk_rate;
                needs_vcc = pdata->needs_vcc;
-               needs_reset = pdata->needs_reset;
+               nop->gpio_reset = pdata->gpio_reset;
        }
 
-       nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
-       if (!nop)
-               return -ENOMEM;
-
-
-       err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc,
-                       needs_reset);
+       err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc);
        if (err)
                return err;
 
@@ -252,15 +265,13 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev)
        if (err) {
                dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
                        err);
-               goto err_add;
+               return err;
        }
 
        platform_set_drvdata(pdev, nop);
 
        return 0;
 
-err_add:
-       usb_phy_gen_cleanup_phy(nop);
        return err;
 }
 
@@ -268,7 +279,6 @@ static int usb_phy_gen_xceiv_remove(struct platform_device *pdev)
 {
        struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev);
 
-       usb_phy_gen_cleanup_phy(nop);
        usb_remove_phy(&nop->phy);
 
        return 0;
index 61687d5a965b24ef69729a9cf0c5c38a48a7a2b5..d2a220d81734ad5be296f56c3b416aa321bfb63e 100644 (file)
@@ -6,15 +6,14 @@ struct usb_phy_gen_xceiv {
        struct device *dev;
        struct clk *clk;
        struct regulator *vcc;
-       struct regulator *reset;
+       int gpio_reset;
+       bool reset_active_low;
 };
 
 int usb_gen_phy_init(struct usb_phy *phy);
 void usb_gen_phy_shutdown(struct usb_phy *phy);
 
 int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
-               enum usb_phy_type type, u32 clk_rate, bool needs_vcc,
-               bool needs_reset);
-void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop);
+               enum usb_phy_type type, u32 clk_rate, bool needs_vcc);
 
 #endif
index a4dda8e1256257104cdfdd3d54707af57361ed8f..09c5ace1edd8a987f015508640f7f461508dd0a9 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/usb/omap_control_usb.h>
 
-static struct omap_control_usb *control_usb;
-
-/**
- * omap_get_control_dev - returns the device pointer for this control device
- *
- * This API should be called to get the device pointer for this control
- * module device. This device pointer should be used for called other
- * exported API's in this driver.
- *
- * To be used by PHY driver and glue driver.
- */
-struct device *omap_get_control_dev(void)
-{
-       if (!control_usb)
-               return ERR_PTR(-ENODEV);
-
-       return control_usb->dev;
-}
-EXPORT_SYMBOL_GPL(omap_get_control_dev);
-
 /**
- * omap_control_usb3_phy_power - power on/off the serializer using control
- *     module
+ * omap_control_usb_phy_power - power on/off the phy using control module reg
  * @dev: the control module device
- * @on: 0 to off and 1 to on based on powering on or off the PHY
- *
- * usb3 PHY driver should call this API to power on or off the PHY.
+ * @on: 0 or 1, based on powering on or off the PHY
  */
-void omap_control_usb3_phy_power(struct device *dev, bool on)
+void omap_control_usb_phy_power(struct device *dev, int on)
 {
        u32 val;
        unsigned long rate;
-       struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+       struct omap_control_usb *control_usb;
 
-       if (control_usb->type != OMAP_CTRL_DEV_TYPE2)
+       if (IS_ERR(dev) || !dev) {
+               pr_err("%s: invalid device\n", __func__);
                return;
+       }
 
-       rate = clk_get_rate(control_usb->sys_clk);
-       rate = rate/1000000;
-
-       val = readl(control_usb->phy_power);
-
-       if (on) {
-               val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
-                       OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
-               val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
-                       OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
-               val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
-       } else {
-               val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK;
-               val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
-                       OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
+       control_usb = dev_get_drvdata(dev);
+       if (!control_usb) {
+               dev_err(dev, "%s: invalid control usb device\n", __func__);
+               return;
        }
 
-       writel(val, control_usb->phy_power);
-}
-EXPORT_SYMBOL_GPL(omap_control_usb3_phy_power);
+       if (control_usb->type == OMAP_CTRL_TYPE_OTGHS)
+               return;
 
-/**
- * omap_control_usb_phy_power - power on/off the phy using control module reg
- * @dev: the control module device
- * @on: 0 or 1, based on powering on or off the PHY
- */
-void omap_control_usb_phy_power(struct device *dev, int on)
-{
-       u32 val;
-       struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+       val = readl(control_usb->power);
 
-       val = readl(control_usb->dev_conf);
+       switch (control_usb->type) {
+       case OMAP_CTRL_TYPE_USB2:
+               if (on)
+                       val &= ~OMAP_CTRL_DEV_PHY_PD;
+               else
+                       val |= OMAP_CTRL_DEV_PHY_PD;
+               break;
 
-       if (on)
-               val &= ~OMAP_CTRL_DEV_PHY_PD;
-       else
-               val |= OMAP_CTRL_DEV_PHY_PD;
+       case OMAP_CTRL_TYPE_PIPE3:
+               rate = clk_get_rate(control_usb->sys_clk);
+               rate = rate/1000000;
+
+               if (on) {
+                       val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
+                                       OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
+                       val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
+                               OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
+                       val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
+               } else {
+                       val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK;
+                       val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
+                               OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
+               }
+               break;
 
-       writel(val, control_usb->dev_conf);
+       case OMAP_CTRL_TYPE_DRA7USB2:
+               if (on)
+                       val &= ~OMAP_CTRL_USB2_PHY_PD;
+               else
+                       val |= OMAP_CTRL_USB2_PHY_PD;
+               break;
+       default:
+               dev_err(dev, "%s: type %d not recognized\n",
+                                       __func__, control_usb->type);
+               break;
+       }
+
+       writel(val, control_usb->power);
 }
 EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
 
@@ -172,11 +162,19 @@ void omap_control_usb_set_mode(struct device *dev,
 {
        struct omap_control_usb *ctrl_usb;
 
-       if (IS_ERR(dev) || control_usb->type != OMAP_CTRL_DEV_TYPE1)
+       if (IS_ERR(dev) || !dev)
                return;
 
        ctrl_usb = dev_get_drvdata(dev);
 
+       if (!ctrl_usb) {
+               dev_err(dev, "Invalid control usb device\n");
+               return;
+       }
+
+       if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS)
+               return;
+
        switch (mode) {
        case USB_MODE_HOST:
                omap_control_usb_host_mode(ctrl_usb);
@@ -193,12 +191,46 @@ void omap_control_usb_set_mode(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
 
+#ifdef CONFIG_OF
+
+static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
+static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2;
+static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
+static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
+
+static const struct of_device_id omap_control_usb_id_table[] = {
+       {
+               .compatible = "ti,control-phy-otghs",
+               .data = &otghs_data,
+       },
+       {
+               .compatible = "ti,control-phy-usb2",
+               .data = &usb2_data,
+       },
+       {
+               .compatible = "ti,control-phy-pipe3",
+               .data = &pipe3_data,
+       },
+       {
+               .compatible = "ti,control-phy-dra7usb2",
+               .data = &dra7usb2_data,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
+#endif
+
+
 static int omap_control_usb_probe(struct platform_device *pdev)
 {
        struct resource *res;
-       struct device_node *np = pdev->dev.of_node;
-       struct omap_control_usb_platform_data *pdata =
-                       dev_get_platdata(&pdev->dev);
+       const struct of_device_id *of_id;
+       struct omap_control_usb *control_usb;
+
+       of_id = of_match_device(of_match_ptr(omap_control_usb_id_table),
+                                                               &pdev->dev);
+       if (!of_id)
+               return -EINVAL;
 
        control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
                GFP_KERNEL);
@@ -207,40 +239,27 @@ static int omap_control_usb_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       if (np) {
-               of_property_read_u32(np, "ti,type", &control_usb->type);
-       } else if (pdata) {
-               control_usb->type = pdata->type;
-       } else {
-               dev_err(&pdev->dev, "no pdata present\n");
-               return -EINVAL;
-       }
-
-       control_usb->dev        = &pdev->dev;
-
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-               "control_dev_conf");
-       control_usb->dev_conf = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(control_usb->dev_conf))
-               return PTR_ERR(control_usb->dev_conf);
+       control_usb->dev = &pdev->dev;
+       control_usb->type = *(enum omap_control_usb_type *)of_id->data;
 
-       if (control_usb->type == OMAP_CTRL_DEV_TYPE1) {
+       if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) {
                res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                        "otghs_control");
                control_usb->otghs_control = devm_ioremap_resource(
                        &pdev->dev, res);
                if (IS_ERR(control_usb->otghs_control))
                        return PTR_ERR(control_usb->otghs_control);
-       }
-
-       if (control_usb->type == OMAP_CTRL_DEV_TYPE2) {
+       } else {
                res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                       "phy_power_usb");
-               control_usb->phy_power = devm_ioremap_resource(
-                       &pdev->dev, res);
-               if (IS_ERR(control_usb->phy_power))
-                       return PTR_ERR(control_usb->phy_power);
+                               "power");
+               control_usb->power = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(control_usb->power)) {
+                       dev_err(&pdev->dev, "Couldn't get power register\n");
+                       return PTR_ERR(control_usb->power);
+               }
+       }
 
+       if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) {
                control_usb->sys_clk = devm_clk_get(control_usb->dev,
                        "sys_clkin");
                if (IS_ERR(control_usb->sys_clk)) {
@@ -249,20 +268,11 @@ static int omap_control_usb_probe(struct platform_device *pdev)
                }
        }
 
-
        dev_set_drvdata(control_usb->dev, control_usb);
 
        return 0;
 }
 
-#ifdef CONFIG_OF
-static const struct of_device_id omap_control_usb_id_table[] = {
-       { .compatible = "ti,omap-control-usb" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
-#endif
-
 static struct platform_driver omap_control_usb_driver = {
        .probe          = omap_control_usb_probe,
        .driver         = {
diff --git a/drivers/usb/phy/phy-omap-usb2.c b/drivers/usb/phy/phy-omap-usb2.c
deleted file mode 100644 (file)
index d266861..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * omap-usb2.c - USB PHY, talking to musb controller in OMAP.
- *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Author: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/io.h>
-#include <linux/usb/omap_usb.h>
-#include <linux/usb/phy_companion.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/pm_runtime.h>
-#include <linux/delay.h>
-#include <linux/usb/omap_control_usb.h>
-
-/**
- * omap_usb2_set_comparator - links the comparator present in the sytem with
- *     this phy
- * @comparator - the companion phy(comparator) for this phy
- *
- * The phy companion driver should call this API passing the phy_companion
- * filled with set_vbus and start_srp to be used by usb phy.
- *
- * For use by phy companion driver
- */
-int omap_usb2_set_comparator(struct phy_companion *comparator)
-{
-       struct omap_usb *phy;
-       struct usb_phy  *x = usb_get_phy(USB_PHY_TYPE_USB2);
-
-       if (IS_ERR(x))
-               return -ENODEV;
-
-       phy = phy_to_omapusb(x);
-       phy->comparator = comparator;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
-
-static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
-{
-       struct omap_usb *phy = phy_to_omapusb(otg->phy);
-
-       if (!phy->comparator)
-               return -ENODEV;
-
-       return phy->comparator->set_vbus(phy->comparator, enabled);
-}
-
-static int omap_usb_start_srp(struct usb_otg *otg)
-{
-       struct omap_usb *phy = phy_to_omapusb(otg->phy);
-
-       if (!phy->comparator)
-               return -ENODEV;
-
-       return phy->comparator->start_srp(phy->comparator);
-}
-
-static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
-{
-       struct usb_phy  *phy = otg->phy;
-
-       otg->host = host;
-       if (!host)
-               phy->state = OTG_STATE_UNDEFINED;
-
-       return 0;
-}
-
-static int omap_usb_set_peripheral(struct usb_otg *otg,
-               struct usb_gadget *gadget)
-{
-       struct usb_phy  *phy = otg->phy;
-
-       otg->gadget = gadget;
-       if (!gadget)
-               phy->state = OTG_STATE_UNDEFINED;
-
-       return 0;
-}
-
-static int omap_usb2_suspend(struct usb_phy *x, int suspend)
-{
-       struct omap_usb *phy = phy_to_omapusb(x);
-       int ret;
-
-       if (suspend && !phy->is_suspended) {
-               omap_control_usb_phy_power(phy->control_dev, 0);
-               pm_runtime_put_sync(phy->dev);
-               phy->is_suspended = 1;
-       } else if (!suspend && phy->is_suspended) {
-               ret = pm_runtime_get_sync(phy->dev);
-               if (ret < 0) {
-                       dev_err(phy->dev, "get_sync failed with err %d\n", ret);
-                       return ret;
-               }
-               omap_control_usb_phy_power(phy->control_dev, 1);
-               phy->is_suspended = 0;
-       }
-
-       return 0;
-}
-
-static int omap_usb2_probe(struct platform_device *pdev)
-{
-       struct omap_usb                 *phy;
-       struct usb_otg                  *otg;
-
-       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
-       if (!phy) {
-               dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
-               return -ENOMEM;
-       }
-
-       otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
-       if (!otg) {
-               dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n");
-               return -ENOMEM;
-       }
-
-       phy->dev                = &pdev->dev;
-
-       phy->phy.dev            = phy->dev;
-       phy->phy.label          = "omap-usb2";
-       phy->phy.set_suspend    = omap_usb2_suspend;
-       phy->phy.otg            = otg;
-       phy->phy.type           = USB_PHY_TYPE_USB2;
-
-       phy->control_dev = omap_get_control_dev();
-       if (IS_ERR(phy->control_dev)) {
-               dev_dbg(&pdev->dev, "Failed to get control device\n");
-               return -ENODEV;
-       }
-
-       phy->is_suspended       = 1;
-       omap_control_usb_phy_power(phy->control_dev, 0);
-
-       otg->set_host           = omap_usb_set_host;
-       otg->set_peripheral     = omap_usb_set_peripheral;
-       otg->set_vbus           = omap_usb_set_vbus;
-       otg->start_srp          = omap_usb_start_srp;
-       otg->phy                = &phy->phy;
-
-       phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
-       if (IS_ERR(phy->wkupclk)) {
-               dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
-               return PTR_ERR(phy->wkupclk);
-       }
-       clk_prepare(phy->wkupclk);
-
-       phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
-       if (IS_ERR(phy->optclk))
-               dev_vdbg(&pdev->dev, "unable to get refclk960m\n");
-       else
-               clk_prepare(phy->optclk);
-
-       usb_add_phy_dev(&phy->phy);
-
-       platform_set_drvdata(pdev, phy);
-
-       pm_runtime_enable(phy->dev);
-
-       return 0;
-}
-
-static int omap_usb2_remove(struct platform_device *pdev)
-{
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-
-       clk_unprepare(phy->wkupclk);
-       if (!IS_ERR(phy->optclk))
-               clk_unprepare(phy->optclk);
-       usb_remove_phy(&phy->phy);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_RUNTIME
-
-static int omap_usb2_runtime_suspend(struct device *dev)
-{
-       struct platform_device  *pdev = to_platform_device(dev);
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-
-       clk_disable(phy->wkupclk);
-       if (!IS_ERR(phy->optclk))
-               clk_disable(phy->optclk);
-
-       return 0;
-}
-
-static int omap_usb2_runtime_resume(struct device *dev)
-{
-       struct platform_device  *pdev = to_platform_device(dev);
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-       int ret;
-
-       ret = clk_enable(phy->wkupclk);
-       if (ret < 0) {
-               dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
-               goto err0;
-       }
-
-       if (!IS_ERR(phy->optclk)) {
-               ret = clk_enable(phy->optclk);
-               if (ret < 0) {
-                       dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
-                       goto err1;
-               }
-       }
-
-       return 0;
-
-err1:
-       clk_disable(phy->wkupclk);
-
-err0:
-       return ret;
-}
-
-static const struct dev_pm_ops omap_usb2_pm_ops = {
-       SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume,
-               NULL)
-};
-
-#define DEV_PM_OPS     (&omap_usb2_pm_ops)
-#else
-#define DEV_PM_OPS     NULL
-#endif
-
-#ifdef CONFIG_OF
-static const struct of_device_id omap_usb2_id_table[] = {
-       { .compatible = "ti,omap-usb2" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
-#endif
-
-static struct platform_driver omap_usb2_driver = {
-       .probe          = omap_usb2_probe,
-       .remove         = omap_usb2_remove,
-       .driver         = {
-               .name   = "omap-usb2",
-               .owner  = THIS_MODULE,
-               .pm     = DEV_PM_OPS,
-               .of_match_table = of_match_ptr(omap_usb2_id_table),
-       },
-};
-
-module_platform_driver(omap_usb2_driver);
-
-MODULE_ALIAS("platform: omap_usb2");
-MODULE_AUTHOR("Texas Instruments Inc.");
-MODULE_DESCRIPTION("OMAP USB2 phy driver");
-MODULE_LICENSE("GPL v2");
index 4e8a0405f956c478491a1ed6f18d65a9afc07e9a..0c6ba29bddddc05b7b3c35e2067c026eec13bc2f 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/delay.h>
 #include <linux/usb/omap_control_usb.h>
+#include <linux/of_platform.h>
 
 #define        PLL_STATUS              0x00000004
 #define        PLL_GO                  0x00000008
@@ -100,7 +101,7 @@ static int omap_usb3_suspend(struct usb_phy *x, int suspend)
                        udelay(1);
                } while (--timeout);
 
-               omap_control_usb3_phy_power(phy->control_dev, 0);
+               omap_control_usb_phy_power(phy->control_dev, 0);
 
                phy->is_suspended       = 1;
        } else if (!suspend && phy->is_suspended) {
@@ -189,15 +190,21 @@ static int omap_usb3_init(struct usb_phy *x)
        if (ret)
                return ret;
 
-       omap_control_usb3_phy_power(phy->control_dev, 1);
+       omap_control_usb_phy_power(phy->control_dev, 1);
 
        return 0;
 }
 
 static int omap_usb3_probe(struct platform_device *pdev)
 {
-       struct omap_usb                 *phy;
-       struct resource                 *res;
+       struct omap_usb *phy;
+       struct resource *res;
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *control_node;
+       struct platform_device *control_pdev;
+
+       if (!node)
+               return -EINVAL;
 
        phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
        if (!phy) {
@@ -239,13 +246,20 @@ static int omap_usb3_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       phy->control_dev = omap_get_control_dev();
-       if (IS_ERR(phy->control_dev)) {
-               dev_dbg(&pdev->dev, "Failed to get control device\n");
-               return -ENODEV;
+       control_node = of_parse_phandle(node, "ctrl-module", 0);
+       if (!control_node) {
+               dev_err(&pdev->dev, "Failed to get control device phandle\n");
+               return -EINVAL;
        }
+       control_pdev = of_find_device_by_node(control_node);
+       if (!control_pdev) {
+               dev_err(&pdev->dev, "Failed to get control device\n");
+               return -EINVAL;
+       }
+
+       phy->control_dev = &control_pdev->dev;
 
-       omap_control_usb3_phy_power(phy->control_dev, 0);
+       omap_control_usb_phy_power(phy->control_dev, 0);
        usb_add_phy_dev(&phy->phy);
 
        platform_set_drvdata(pdev, phy);
diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c
new file mode 100644 (file)
index 0000000..a99a695
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Renesas R-Car Gen2 USB phy driver
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_data/usb-rcar-gen2-phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/usb/otg.h>
+
+struct rcar_gen2_usb_phy_priv {
+       struct usb_phy phy;
+       void __iomem *base;
+       struct clk *clk;
+       spinlock_t lock;
+       int usecount;
+       u32 ugctrl2;
+};
+
+#define usb_phy_to_priv(p) container_of(p, struct rcar_gen2_usb_phy_priv, phy)
+
+/* Low Power Status register */
+#define USBHS_LPSTS_REG                        0x02
+#define USBHS_LPSTS_SUSPM              (1 << 14)
+
+/* USB General control register */
+#define USBHS_UGCTRL_REG               0x80
+#define USBHS_UGCTRL_CONNECT           (1 << 2)
+#define USBHS_UGCTRL_PLLRESET          (1 << 0)
+
+/* USB General control register 2 */
+#define USBHS_UGCTRL2_REG              0x84
+#define USBHS_UGCTRL2_USB0_PCI         (1 << 4)
+#define USBHS_UGCTRL2_USB0_HS          (3 << 4)
+#define USBHS_UGCTRL2_USB2_PCI         (0 << 31)
+#define USBHS_UGCTRL2_USB2_SS          (1 << 31)
+
+/* USB General status register */
+#define USBHS_UGSTS_REG                        0x88
+#define USBHS_UGSTS_LOCK               (3 << 8)
+
+/* Enable USBHS internal phy */
+static int __rcar_gen2_usbhs_phy_enable(void __iomem *base)
+{
+       u32 val;
+       int i;
+
+       /* USBHS PHY power on */
+       val = ioread32(base + USBHS_UGCTRL_REG);
+       val &= ~USBHS_UGCTRL_PLLRESET;
+       iowrite32(val, base + USBHS_UGCTRL_REG);
+
+       val = ioread16(base + USBHS_LPSTS_REG);
+       val |= USBHS_LPSTS_SUSPM;
+       iowrite16(val, base + USBHS_LPSTS_REG);
+
+       for (i = 0; i < 20; i++) {
+               val = ioread32(base + USBHS_UGSTS_REG);
+               if ((val & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) {
+                       val = ioread32(base + USBHS_UGCTRL_REG);
+                       val |= USBHS_UGCTRL_CONNECT;
+                       iowrite32(val, base + USBHS_UGCTRL_REG);
+                       return 0;
+               }
+               udelay(1);
+       }
+
+       /* Timed out waiting for the PLL lock */
+       return -ETIMEDOUT;
+}
+
+/* Disable USBHS internal phy */
+static int __rcar_gen2_usbhs_phy_disable(void __iomem *base)
+{
+       u32 val;
+
+       /* USBHS PHY power off */
+       val = ioread32(base + USBHS_UGCTRL_REG);
+       val &= ~USBHS_UGCTRL_CONNECT;
+       iowrite32(val, base + USBHS_UGCTRL_REG);
+
+       val = ioread16(base + USBHS_LPSTS_REG);
+       val &= ~USBHS_LPSTS_SUSPM;
+       iowrite16(val, base + USBHS_LPSTS_REG);
+
+       val = ioread32(base + USBHS_UGCTRL_REG);
+       val |= USBHS_UGCTRL_PLLRESET;
+       iowrite32(val, base + USBHS_UGCTRL_REG);
+       return 0;
+}
+
+/* Setup USB channels */
+static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv)
+{
+       u32 val;
+
+       clk_prepare_enable(priv->clk);
+
+       /* Set USB channels in the USBHS UGCTRL2 register */
+       val = ioread32(priv->base);
+       val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS);
+       val |= priv->ugctrl2;
+       iowrite32(val, priv->base);
+}
+
+/* Shutdown USB channels */
+static void __rcar_gen2_usb_phy_shutdown(struct rcar_gen2_usb_phy_priv *priv)
+{
+       __rcar_gen2_usbhs_phy_disable(priv->base);
+       clk_disable_unprepare(priv->clk);
+}
+
+static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend)
+{
+       struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy);
+       unsigned long flags;
+       int retval;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       retval = suspend ? __rcar_gen2_usbhs_phy_disable(priv->base) :
+                          __rcar_gen2_usbhs_phy_enable(priv->base);
+       spin_unlock_irqrestore(&priv->lock, flags);
+       return retval;
+}
+
+static int rcar_gen2_usb_phy_init(struct usb_phy *phy)
+{
+       struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       /*
+        * Enable the clock and setup USB channels
+        * if it's the first user
+        */
+       if (!priv->usecount++)
+               __rcar_gen2_usb_phy_init(priv);
+       spin_unlock_irqrestore(&priv->lock, flags);
+       return 0;
+}
+
+static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy)
+{
+       struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (!priv->usecount) {
+               dev_warn(phy->dev, "Trying to disable phy with 0 usecount\n");
+               goto out;
+       }
+
+       /* Disable everything if it's the last user */
+       if (!--priv->usecount)
+               __rcar_gen2_usb_phy_shutdown(priv);
+out:
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int rcar_gen2_usb_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rcar_gen2_phy_platform_data *pdata;
+       struct rcar_gen2_usb_phy_priv *priv;
+       struct resource *res;
+       void __iomem *base;
+       struct clk *clk;
+       int retval;
+
+       pdata = dev_get_platdata(&pdev->dev);
+       if (!pdata) {
+               dev_err(dev, "No platform data\n");
+               return -EINVAL;
+       }
+
+       clk = devm_clk_get(&pdev->dev, "usbhs");
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "Can't get the clock\n");
+               return PTR_ERR(clk);
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(dev, "Memory allocation failed\n");
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&priv->lock);
+       priv->clk = clk;
+       priv->base = base;
+       priv->ugctrl2 = pdata->chan0_pci ?
+                       USBHS_UGCTRL2_USB0_PCI : USBHS_UGCTRL2_USB0_HS;
+       priv->ugctrl2 |= pdata->chan2_pci ?
+                       USBHS_UGCTRL2_USB2_PCI : USBHS_UGCTRL2_USB2_SS;
+       priv->phy.dev = dev;
+       priv->phy.label = dev_name(dev);
+       priv->phy.init = rcar_gen2_usb_phy_init;
+       priv->phy.shutdown = rcar_gen2_usb_phy_shutdown;
+       priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend;
+
+       retval = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2);
+       if (retval < 0) {
+               dev_err(dev, "Failed to add USB phy\n");
+               return retval;
+       }
+
+       platform_set_drvdata(pdev, priv);
+
+       return retval;
+}
+
+static int rcar_gen2_usb_phy_remove(struct platform_device *pdev)
+{
+       struct rcar_gen2_usb_phy_priv *priv = platform_get_drvdata(pdev);
+
+       usb_remove_phy(&priv->phy);
+
+       return 0;
+}
+
+static struct platform_driver rcar_gen2_usb_phy_driver = {
+       .driver = {
+               .name = "usb_phy_rcar_gen2",
+       },
+       .probe = rcar_gen2_usb_phy_probe,
+       .remove = rcar_gen2_usb_phy_remove,
+};
+
+module_platform_driver(rcar_gen2_usb_phy_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas R-Car Gen2 USB phy");
+MODULE_AUTHOR("Valentine Barshak <valentine.barshak@cogentembedded.com>");
index ff70e4b19b9705cc4b69b1ea0de7b5c1ecf23959..b3ba86627b7295960e5c0d8701f1b7fd887a98da 100644 (file)
@@ -411,6 +411,7 @@ static int samsung_usb2phy_probe(struct platform_device *pdev)
        sphy->drv_data          = drv_data;
        sphy->phy.dev           = sphy->dev;
        sphy->phy.label         = "samsung-usb2phy";
+       sphy->phy.type          = USB_PHY_TYPE_USB2;
        sphy->phy.init          = samsung_usb2phy_init;
        sphy->phy.shutdown      = samsung_usb2phy_shutdown;
 
@@ -426,7 +427,7 @@ static int samsung_usb2phy_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, sphy);
 
-       return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2);
+       return usb_add_phy_dev(&sphy->phy);
 }
 
 static int samsung_usb2phy_remove(struct platform_device *pdev)
index c6eb22213de607c3a5997b7a80a92d814eb371bd..cc0819248acfc6633fbe524f8f0346152bb5d275 100644 (file)
@@ -271,6 +271,7 @@ static int samsung_usb3phy_probe(struct platform_device *pdev)
        sphy->clk               = clk;
        sphy->phy.dev           = sphy->dev;
        sphy->phy.label         = "samsung-usb3phy";
+       sphy->phy.type          = USB_PHY_TYPE_USB3;
        sphy->phy.init          = samsung_usb3phy_init;
        sphy->phy.shutdown      = samsung_usb3phy_shutdown;
        sphy->drv_data          = samsung_usbphy_get_driver_data(pdev);
@@ -283,7 +284,7 @@ static int samsung_usb3phy_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, sphy);
 
-       return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB3);
+       return usb_add_phy_dev(&sphy->phy);
 }
 
 static int samsung_usb3phy_remove(struct platform_device *pdev)
index e9cb1cb8abc7386c7f9e19a5f9c64adcfd271a96..82232acf1ab61b17cfbe9d084c3ed2188d554b18 100644 (file)
@@ -1090,7 +1090,7 @@ static struct platform_driver tegra_usb_phy_driver = {
        .driver         = {
                .name   = "tegra-phy",
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(tegra_usb_phy_id_table),
+               .of_match_table = tegra_usb_phy_id_table,
        },
 };
 module_platform_driver(tegra_usb_phy_driver);
diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c
deleted file mode 100644 (file)
index 90730c8..0000000
+++ /dev/null
@@ -1,794 +0,0 @@
-/*
- * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller
- *
- * Copyright (C) 2004-2007 Texas Instruments
- * Copyright (C) 2008 Nokia Corporation
- * Contact: Felipe Balbi <felipe.balbi@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Current status:
- *     - HS USB ULPI mode works.
- *     - 3-pin mode support may be added in future.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/usb/otg.h>
-#include <linux/usb/musb-omap.h>
-#include <linux/usb/ulpi.h>
-#include <linux/i2c/twl.h>
-#include <linux/regulator/consumer.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-
-/* Register defines */
-
-#define MCPC_CTRL                      0x30
-#define MCPC_CTRL_RTSOL                        (1 << 7)
-#define MCPC_CTRL_EXTSWR               (1 << 6)
-#define MCPC_CTRL_EXTSWC               (1 << 5)
-#define MCPC_CTRL_VOICESW              (1 << 4)
-#define MCPC_CTRL_OUT64K               (1 << 3)
-#define MCPC_CTRL_RTSCTSSW             (1 << 2)
-#define MCPC_CTRL_HS_UART              (1 << 0)
-
-#define MCPC_IO_CTRL                   0x33
-#define MCPC_IO_CTRL_MICBIASEN         (1 << 5)
-#define MCPC_IO_CTRL_CTS_NPU           (1 << 4)
-#define MCPC_IO_CTRL_RXD_PU            (1 << 3)
-#define MCPC_IO_CTRL_TXDTYP            (1 << 2)
-#define MCPC_IO_CTRL_CTSTYP            (1 << 1)
-#define MCPC_IO_CTRL_RTSTYP            (1 << 0)
-
-#define MCPC_CTRL2                     0x36
-#define MCPC_CTRL2_MCPC_CK_EN          (1 << 0)
-
-#define OTHER_FUNC_CTRL                        0x80
-#define OTHER_FUNC_CTRL_BDIS_ACON_EN   (1 << 4)
-#define OTHER_FUNC_CTRL_FIVEWIRE_MODE  (1 << 2)
-
-#define OTHER_IFC_CTRL                 0x83
-#define OTHER_IFC_CTRL_OE_INT_EN       (1 << 6)
-#define OTHER_IFC_CTRL_CEA2011_MODE    (1 << 5)
-#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN     (1 << 4)
-#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT      (1 << 3)
-#define OTHER_IFC_CTRL_HIZ_ULPI                (1 << 2)
-#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0)
-
-#define OTHER_INT_EN_RISE              0x86
-#define OTHER_INT_EN_FALL              0x89
-#define OTHER_INT_STS                  0x8C
-#define OTHER_INT_LATCH                        0x8D
-#define OTHER_INT_VB_SESS_VLD          (1 << 7)
-#define OTHER_INT_DM_HI                        (1 << 6) /* not valid for "latch" reg */
-#define OTHER_INT_DP_HI                        (1 << 5) /* not valid for "latch" reg */
-#define OTHER_INT_BDIS_ACON            (1 << 3) /* not valid for "fall" regs */
-#define OTHER_INT_MANU                 (1 << 1)
-#define OTHER_INT_ABNORMAL_STRESS      (1 << 0)
-
-#define ID_STATUS                      0x96
-#define ID_RES_FLOAT                   (1 << 4)
-#define ID_RES_440K                    (1 << 3)
-#define ID_RES_200K                    (1 << 2)
-#define ID_RES_102K                    (1 << 1)
-#define ID_RES_GND                     (1 << 0)
-
-#define POWER_CTRL                     0xAC
-#define POWER_CTRL_OTG_ENAB            (1 << 5)
-
-#define OTHER_IFC_CTRL2                        0xAF
-#define OTHER_IFC_CTRL2_ULPI_STP_LOW   (1 << 4)
-#define OTHER_IFC_CTRL2_ULPI_TXEN_POL  (1 << 3)
-#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2)
-#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK    (3 << 0) /* bits 0 and 1 */
-#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N   (0 << 0)
-#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N   (1 << 0)
-
-#define REG_CTRL_EN                    0xB2
-#define REG_CTRL_ERROR                 0xB5
-#define ULPI_I2C_CONFLICT_INTEN                (1 << 0)
-
-#define OTHER_FUNC_CTRL2               0xB8
-#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0)
-
-/* following registers do not have separate _clr and _set registers */
-#define VBUS_DEBOUNCE                  0xC0
-#define ID_DEBOUNCE                    0xC1
-#define VBAT_TIMER                     0xD3
-#define PHY_PWR_CTRL                   0xFD
-#define PHY_PWR_PHYPWD                 (1 << 0)
-#define PHY_CLK_CTRL                   0xFE
-#define PHY_CLK_CTRL_CLOCKGATING_EN    (1 << 2)
-#define PHY_CLK_CTRL_CLK32K_EN         (1 << 1)
-#define REQ_PHY_DPLL_CLK               (1 << 0)
-#define PHY_CLK_CTRL_STS               0xFF
-#define PHY_DPLL_CLK                   (1 << 0)
-
-/* In module TWL_MODULE_PM_MASTER */
-#define STS_HW_CONDITIONS              0x0F
-
-/* In module TWL_MODULE_PM_RECEIVER */
-#define VUSB_DEDICATED1                        0x7D
-#define VUSB_DEDICATED2                        0x7E
-#define VUSB1V5_DEV_GRP                        0x71
-#define VUSB1V5_TYPE                   0x72
-#define VUSB1V5_REMAP                  0x73
-#define VUSB1V8_DEV_GRP                        0x74
-#define VUSB1V8_TYPE                   0x75
-#define VUSB1V8_REMAP                  0x76
-#define VUSB3V1_DEV_GRP                        0x77
-#define VUSB3V1_TYPE                   0x78
-#define VUSB3V1_REMAP                  0x79
-
-/* In module TWL4030_MODULE_INTBR */
-#define PMBR1                          0x0D
-#define GPIO_USB_4PIN_ULPI_2430C       (3 << 0)
-
-struct twl4030_usb {
-       struct usb_phy          phy;
-       struct device           *dev;
-
-       /* TWL4030 internal USB regulator supplies */
-       struct regulator        *usb1v5;
-       struct regulator        *usb1v8;
-       struct regulator        *usb3v1;
-
-       /* for vbus reporting with irqs disabled */
-       spinlock_t              lock;
-
-       /* pin configuration */
-       enum twl4030_usb_mode   usb_mode;
-
-       int                     irq;
-       enum omap_musb_vbus_id_status linkstat;
-       bool                    vbus_supplied;
-       u8                      asleep;
-       bool                    irq_enabled;
-
-       struct delayed_work     id_workaround_work;
-};
-
-/* internal define on top of container_of */
-#define phy_to_twl(x)          container_of((x), struct twl4030_usb, phy)
-
-/*-------------------------------------------------------------------------*/
-
-static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl,
-               u8 module, u8 data, u8 address)
-{
-       u8 check;
-
-       if ((twl_i2c_write_u8(module, data, address) >= 0) &&
-           (twl_i2c_read_u8(module, &check, address) >= 0) &&
-                                               (check == data))
-               return 0;
-       dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
-                       1, module, address, check, data);
-
-       /* Failed once: Try again */
-       if ((twl_i2c_write_u8(module, data, address) >= 0) &&
-           (twl_i2c_read_u8(module, &check, address) >= 0) &&
-                                               (check == data))
-               return 0;
-       dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
-                       2, module, address, check, data);
-
-       /* Failed again: Return error */
-       return -EBUSY;
-}
-
-#define twl4030_usb_write_verify(twl, address, data)   \
-       twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address))
-
-static inline int twl4030_usb_write(struct twl4030_usb *twl,
-               u8 address, u8 data)
-{
-       int ret = 0;
-
-       ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address);
-       if (ret < 0)
-               dev_dbg(twl->dev,
-                       "TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
-       return ret;
-}
-
-static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address)
-{
-       u8 data;
-       int ret = 0;
-
-       ret = twl_i2c_read_u8(module, &data, address);
-       if (ret >= 0)
-               ret = data;
-       else
-               dev_dbg(twl->dev,
-                       "TWL4030:readb[0x%x,0x%x] Error %d\n",
-                                       module, address, ret);
-
-       return ret;
-}
-
-static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address)
-{
-       return twl4030_readb(twl, TWL_MODULE_USB, address);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static inline int
-twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
-{
-       return twl4030_usb_write(twl, ULPI_SET(reg), bits);
-}
-
-static inline int
-twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
-{
-       return twl4030_usb_write(twl, ULPI_CLR(reg), bits);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static bool twl4030_is_driving_vbus(struct twl4030_usb *twl)
-{
-       int ret;
-
-       ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS);
-       if (ret < 0 || !(ret & PHY_DPLL_CLK))
-               /*
-                * if clocks are off, registers are not updated,
-                * but we can assume we don't drive VBUS in this case
-                */
-               return false;
-
-       ret = twl4030_usb_read(twl, ULPI_OTG_CTRL);
-       if (ret < 0)
-               return false;
-
-       return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false;
-}
-
-static enum omap_musb_vbus_id_status
-       twl4030_usb_linkstat(struct twl4030_usb *twl)
-{
-       int     status;
-       enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
-
-       twl->vbus_supplied = false;
-
-       /*
-        * For ID/VBUS sensing, see manual section 15.4.8 ...
-        * except when using only battery backup power, two
-        * comparators produce VBUS_PRES and ID_PRES signals,
-        * which don't match docs elsewhere.  But ... BIT(7)
-        * and BIT(2) of STS_HW_CONDITIONS, respectively, do
-        * seem to match up.  If either is true the USB_PRES
-        * signal is active, the OTG module is activated, and
-        * its interrupt may be raised (may wake the system).
-        */
-       status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS);
-       if (status < 0)
-               dev_err(twl->dev, "USB link status err %d\n", status);
-       else if (status & (BIT(7) | BIT(2))) {
-               if (status & BIT(7)) {
-                       if (twl4030_is_driving_vbus(twl))
-                               status &= ~BIT(7);
-                       else
-                               twl->vbus_supplied = true;
-               }
-
-               if (status & BIT(2))
-                       linkstat = OMAP_MUSB_ID_GROUND;
-               else if (status & BIT(7))
-                       linkstat = OMAP_MUSB_VBUS_VALID;
-               else
-                       linkstat = OMAP_MUSB_VBUS_OFF;
-       } else {
-               if (twl->linkstat != OMAP_MUSB_UNKNOWN)
-                       linkstat = OMAP_MUSB_VBUS_OFF;
-       }
-
-       dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
-                       status, status, linkstat);
-
-       /* REVISIT this assumes host and peripheral controllers
-        * are registered, and that both are active...
-        */
-
-       return linkstat;
-}
-
-static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
-{
-       twl->usb_mode = mode;
-
-       switch (mode) {
-       case T2_USB_MODE_ULPI:
-               twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL,
-                                       ULPI_IFC_CTRL_CARKITMODE);
-               twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
-               twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL,
-                                       ULPI_FUNC_CTRL_XCVRSEL_MASK |
-                                       ULPI_FUNC_CTRL_OPMODE_MASK);
-               break;
-       case -1:
-               /* FIXME: power on defaults */
-               break;
-       default:
-               dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
-                               mode);
-               break;
-       };
-}
-
-static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
-{
-       unsigned long timeout;
-       int val = twl4030_usb_read(twl, PHY_CLK_CTRL);
-
-       if (val >= 0) {
-               if (on) {
-                       /* enable DPLL to access PHY registers over I2C */
-                       val |= REQ_PHY_DPLL_CLK;
-                       WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
-                                               (u8)val) < 0);
-
-                       timeout = jiffies + HZ;
-                       while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
-                                                       PHY_DPLL_CLK)
-                               && time_before(jiffies, timeout))
-                                       udelay(10);
-                       if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
-                                                       PHY_DPLL_CLK))
-                               dev_err(twl->dev, "Timeout setting T2 HSUSB "
-                                               "PHY DPLL clock\n");
-               } else {
-                       /* let ULPI control the DPLL clock */
-                       val &= ~REQ_PHY_DPLL_CLK;
-                       WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
-                                               (u8)val) < 0);
-               }
-       }
-}
-
-static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
-{
-       u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
-
-       if (on)
-               pwr &= ~PHY_PWR_PHYPWD;
-       else
-               pwr |= PHY_PWR_PHYPWD;
-
-       WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
-}
-
-static void twl4030_phy_power(struct twl4030_usb *twl, int on)
-{
-       int ret;
-
-       if (on) {
-               ret = regulator_enable(twl->usb3v1);
-               if (ret)
-                       dev_err(twl->dev, "Failed to enable usb3v1\n");
-
-               ret = regulator_enable(twl->usb1v8);
-               if (ret)
-                       dev_err(twl->dev, "Failed to enable usb1v8\n");
-
-               /*
-                * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP
-                * in twl4030) resets the VUSB_DEDICATED2 register. This reset
-                * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to
-                * SLEEP. We work around this by clearing the bit after usv3v1
-                * is re-activated. This ensures that VUSB3V1 is really active.
-                */
-               twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
-
-               ret = regulator_enable(twl->usb1v5);
-               if (ret)
-                       dev_err(twl->dev, "Failed to enable usb1v5\n");
-
-               __twl4030_phy_power(twl, 1);
-               twl4030_usb_write(twl, PHY_CLK_CTRL,
-                                 twl4030_usb_read(twl, PHY_CLK_CTRL) |
-                                       (PHY_CLK_CTRL_CLOCKGATING_EN |
-                                               PHY_CLK_CTRL_CLK32K_EN));
-       } else {
-               __twl4030_phy_power(twl, 0);
-               regulator_disable(twl->usb1v5);
-               regulator_disable(twl->usb1v8);
-               regulator_disable(twl->usb3v1);
-       }
-}
-
-static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off)
-{
-       if (twl->asleep)
-               return;
-
-       twl4030_phy_power(twl, 0);
-       twl->asleep = 1;
-       dev_dbg(twl->dev, "%s\n", __func__);
-}
-
-static void __twl4030_phy_resume(struct twl4030_usb *twl)
-{
-       twl4030_phy_power(twl, 1);
-       twl4030_i2c_access(twl, 1);
-       twl4030_usb_set_mode(twl, twl->usb_mode);
-       if (twl->usb_mode == T2_USB_MODE_ULPI)
-               twl4030_i2c_access(twl, 0);
-}
-
-static void twl4030_phy_resume(struct twl4030_usb *twl)
-{
-       if (!twl->asleep)
-               return;
-       __twl4030_phy_resume(twl);
-       twl->asleep = 0;
-       dev_dbg(twl->dev, "%s\n", __func__);
-
-       /*
-        * XXX When VBUS gets driven after musb goes to A mode,
-        * ID_PRES related interrupts no longer arrive, why?
-        * Register itself is updated fine though, so we must poll.
-        */
-       if (twl->linkstat == OMAP_MUSB_ID_GROUND) {
-               cancel_delayed_work(&twl->id_workaround_work);
-               schedule_delayed_work(&twl->id_workaround_work, HZ);
-       }
-}
-
-static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
-{
-       /* Enable writing to power configuration registers */
-       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
-                        TWL4030_PM_MASTER_PROTECT_KEY);
-
-       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2,
-                        TWL4030_PM_MASTER_PROTECT_KEY);
-
-       /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/
-       /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/
-
-       /* input to VUSB3V1 LDO is from VBAT, not VBUS */
-       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
-
-       /* Initialize 3.1V regulator */
-       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
-
-       twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1");
-       if (IS_ERR(twl->usb3v1))
-               return -ENODEV;
-
-       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
-
-       /* Initialize 1.5V regulator */
-       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
-
-       twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5");
-       if (IS_ERR(twl->usb1v5))
-               return -ENODEV;
-
-       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
-
-       /* Initialize 1.8V regulator */
-       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
-
-       twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8");
-       if (IS_ERR(twl->usb1v8))
-               return -ENODEV;
-
-       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
-
-       /* disable access to power configuration registers */
-       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
-                        TWL4030_PM_MASTER_PROTECT_KEY);
-
-       return 0;
-}
-
-static ssize_t twl4030_usb_vbus_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct twl4030_usb *twl = dev_get_drvdata(dev);
-       unsigned long flags;
-       int ret = -EINVAL;
-
-       spin_lock_irqsave(&twl->lock, flags);
-       ret = sprintf(buf, "%s\n",
-                       twl->vbus_supplied ? "on" : "off");
-       spin_unlock_irqrestore(&twl->lock, flags);
-
-       return ret;
-}
-static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
-
-static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
-{
-       struct twl4030_usb *twl = _twl;
-       enum omap_musb_vbus_id_status status;
-       bool status_changed = false;
-
-       status = twl4030_usb_linkstat(twl);
-
-       spin_lock_irq(&twl->lock);
-       if (status >= 0 && status != twl->linkstat) {
-               twl->linkstat = status;
-               status_changed = true;
-       }
-       spin_unlock_irq(&twl->lock);
-
-       if (status_changed) {
-               /* FIXME add a set_power() method so that B-devices can
-                * configure the charger appropriately.  It's not always
-                * correct to consume VBUS power, and how much current to
-                * consume is a function of the USB configuration chosen
-                * by the host.
-                *
-                * REVISIT usb_gadget_vbus_connect(...) as needed, ditto
-                * its disconnect() sibling, when changing to/from the
-                * USB_LINK_VBUS state.  musb_hdrc won't care until it
-                * starts to handle softconnect right.
-                */
-               omap_musb_mailbox(status);
-       }
-       sysfs_notify(&twl->dev->kobj, NULL, "vbus");
-
-       return IRQ_HANDLED;
-}
-
-static void twl4030_id_workaround_work(struct work_struct *work)
-{
-       struct twl4030_usb *twl = container_of(work, struct twl4030_usb,
-               id_workaround_work.work);
-       enum omap_musb_vbus_id_status status;
-       bool status_changed = false;
-
-       status = twl4030_usb_linkstat(twl);
-
-       spin_lock_irq(&twl->lock);
-       if (status >= 0 && status != twl->linkstat) {
-               twl->linkstat = status;
-               status_changed = true;
-       }
-       spin_unlock_irq(&twl->lock);
-
-       if (status_changed) {
-               dev_dbg(twl->dev, "handle missing status change to %d\n",
-                               status);
-               omap_musb_mailbox(status);
-       }
-
-       /* don't schedule during sleep - irq works right then */
-       if (status == OMAP_MUSB_ID_GROUND && !twl->asleep) {
-               cancel_delayed_work(&twl->id_workaround_work);
-               schedule_delayed_work(&twl->id_workaround_work, HZ);
-       }
-}
-
-static int twl4030_usb_phy_init(struct usb_phy *phy)
-{
-       struct twl4030_usb *twl = phy_to_twl(phy);
-       enum omap_musb_vbus_id_status status;
-
-       /*
-        * Start in sleep state, we'll get called through set_suspend()
-        * callback when musb is runtime resumed and it's time to start.
-        */
-       __twl4030_phy_power(twl, 0);
-       twl->asleep = 1;
-
-       status = twl4030_usb_linkstat(twl);
-       twl->linkstat = status;
-
-       if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID)
-               omap_musb_mailbox(twl->linkstat);
-
-       sysfs_notify(&twl->dev->kobj, NULL, "vbus");
-       return 0;
-}
-
-static int twl4030_set_suspend(struct usb_phy *x, int suspend)
-{
-       struct twl4030_usb *twl = phy_to_twl(x);
-
-       if (suspend)
-               twl4030_phy_suspend(twl, 1);
-       else
-               twl4030_phy_resume(twl);
-
-       return 0;
-}
-
-static int twl4030_set_peripheral(struct usb_otg *otg,
-                                       struct usb_gadget *gadget)
-{
-       if (!otg)
-               return -ENODEV;
-
-       otg->gadget = gadget;
-       if (!gadget)
-               otg->phy->state = OTG_STATE_UNDEFINED;
-
-       return 0;
-}
-
-static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
-{
-       if (!otg)
-               return -ENODEV;
-
-       otg->host = host;
-       if (!host)
-               otg->phy->state = OTG_STATE_UNDEFINED;
-
-       return 0;
-}
-
-static int twl4030_usb_probe(struct platform_device *pdev)
-{
-       struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev);
-       struct twl4030_usb      *twl;
-       int                     status, err;
-       struct usb_otg          *otg;
-       struct device_node      *np = pdev->dev.of_node;
-
-       twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
-       if (!twl)
-               return -ENOMEM;
-
-       if (np)
-               of_property_read_u32(np, "usb_mode",
-                               (enum twl4030_usb_mode *)&twl->usb_mode);
-       else if (pdata)
-               twl->usb_mode = pdata->usb_mode;
-       else {
-               dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
-               return -EINVAL;
-       }
-
-       otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
-       if (!otg)
-               return -ENOMEM;
-
-       twl->dev                = &pdev->dev;
-       twl->irq                = platform_get_irq(pdev, 0);
-       twl->vbus_supplied      = false;
-       twl->asleep             = 1;
-       twl->linkstat           = OMAP_MUSB_UNKNOWN;
-
-       twl->phy.dev            = twl->dev;
-       twl->phy.label          = "twl4030";
-       twl->phy.otg            = otg;
-       twl->phy.type           = USB_PHY_TYPE_USB2;
-       twl->phy.set_suspend    = twl4030_set_suspend;
-       twl->phy.init           = twl4030_usb_phy_init;
-
-       otg->phy                = &twl->phy;
-       otg->set_host           = twl4030_set_host;
-       otg->set_peripheral     = twl4030_set_peripheral;
-
-       /* init spinlock for workqueue */
-       spin_lock_init(&twl->lock);
-
-       INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work);
-
-       err = twl4030_usb_ldo_init(twl);
-       if (err) {
-               dev_err(&pdev->dev, "ldo init failed\n");
-               return err;
-       }
-       usb_add_phy_dev(&twl->phy);
-
-       platform_set_drvdata(pdev, twl);
-       if (device_create_file(&pdev->dev, &dev_attr_vbus))
-               dev_warn(&pdev->dev, "could not create sysfs file\n");
-
-       /* Our job is to use irqs and status from the power module
-        * to keep the transceiver disabled when nothing's connected.
-        *
-        * FIXME we actually shouldn't start enabling it until the
-        * USB controller drivers have said they're ready, by calling
-        * set_host() and/or set_peripheral() ... OTG_capable boards
-        * need both handles, otherwise just one suffices.
-        */
-       twl->irq_enabled = true;
-       status = devm_request_threaded_irq(twl->dev, twl->irq, NULL,
-                       twl4030_usb_irq, IRQF_TRIGGER_FALLING |
-                       IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl);
-       if (status < 0) {
-               dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
-                       twl->irq, status);
-               return status;
-       }
-
-       dev_info(&pdev->dev, "Initialized TWL4030 USB module\n");
-       return 0;
-}
-
-static int twl4030_usb_remove(struct platform_device *pdev)
-{
-       struct twl4030_usb *twl = platform_get_drvdata(pdev);
-       int val;
-
-       cancel_delayed_work(&twl->id_workaround_work);
-       device_remove_file(twl->dev, &dev_attr_vbus);
-
-       /* set transceiver mode to power on defaults */
-       twl4030_usb_set_mode(twl, -1);
-
-       /* autogate 60MHz ULPI clock,
-        * clear dpll clock request for i2c access,
-        * disable 32KHz
-        */
-       val = twl4030_usb_read(twl, PHY_CLK_CTRL);
-       if (val >= 0) {
-               val |= PHY_CLK_CTRL_CLOCKGATING_EN;
-               val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK);
-               twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val);
-       }
-
-       /* disable complete OTG block */
-       twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
-
-       if (!twl->asleep)
-               twl4030_phy_power(twl, 0);
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id twl4030_usb_id_table[] = {
-       { .compatible = "ti,twl4030-usb" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, twl4030_usb_id_table);
-#endif
-
-static struct platform_driver twl4030_usb_driver = {
-       .probe          = twl4030_usb_probe,
-       .remove         = twl4030_usb_remove,
-       .driver         = {
-               .name   = "twl4030_usb",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(twl4030_usb_id_table),
-       },
-};
-
-static int __init twl4030_usb_init(void)
-{
-       return platform_driver_register(&twl4030_usb_driver);
-}
-subsys_initcall(twl4030_usb_init);
-
-static void __exit twl4030_usb_exit(void)
-{
-       platform_driver_unregister(&twl4030_usb_driver);
-}
-module_exit(twl4030_usb_exit);
-
-MODULE_ALIAS("platform:twl4030_usb");
-MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation");
-MODULE_DESCRIPTION("TWL4030 USB transceiver driver");
-MODULE_LICENSE("GPL");
index 16dbc93826789dc22d0a2d955bc4c880cf9196c5..30e8a61552d4d0526d999c0b49651593f04a5530 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/of.h>
 
 /* usb register definitions */
 #define USB_VENDOR_ID_LSB              0x00
index 7c22a5390fc34dddf8e6f9987da559d32732a406..18bb8264b5a0565e91f714df9544fa8f27b9b189 100644 (file)
@@ -36,7 +36,7 @@ static int ulpi_viewport_wait(void __iomem *view, u32 mask)
                        return 0;
 
                udelay(1);
-       };
+       }
 
        return -ETIMEDOUT;
 }
index a9984c700d2c428fd2e771a1d175c12009f94475..1b74523e1fee5640bad21035b171ef9cf2c5289b 100644 (file)
@@ -98,7 +98,7 @@ struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type)
 
        ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
        if (!ptr)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        phy = usb_get_phy(type);
        if (!IS_ERR(phy)) {
index 781426230d695a3f34544ab4b0a589a500a0063e..6e1b69d0f5f57da980f439a3643567ada4aec3b6 100644 (file)
@@ -279,7 +279,7 @@ static void cyberjack_read_int_callback(struct urb *urb)
 
                old_rdtodo = priv->rdtodo;
 
-               if (old_rdtodo + size < old_rdtodo) {
+               if (old_rdtodo > SHRT_MAX - size) {
                        dev_dbg(dev, "To many bulk_in urbs to do.\n");
                        spin_unlock(&priv->lock);
                        goto resubmit;
index c45f9c0a1b3493f8f5566c87d6b8702f9fc8116c..9ced8937a8f3a699dad340e1e9ea62c9d706dc69 100644 (file)
@@ -904,6 +904,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
        /* Crucible Devices */
        { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_Z3X_PID) },
        { }                                     /* Terminating entry */
 };
 
@@ -1966,8 +1967,16 @@ static int ftdi_process_packet(struct usb_serial_port *port,
                        port->icount.dsr++;
                if (diff_status & FTDI_RS0_RI)
                        port->icount.rng++;
-               if (diff_status & FTDI_RS0_RLSD)
+               if (diff_status & FTDI_RS0_RLSD) {
+                       struct tty_struct *tty;
+
                        port->icount.dcd++;
+                       tty = tty_port_tty_get(&port->port);
+                       if (tty)
+                               usb_serial_handle_dcd_change(port, tty,
+                                               status & FTDI_RS0_RLSD);
+                       tty_kref_put(tty);
+               }
 
                wake_up_interruptible(&port->port.delta_msr_wait);
                priv->prev_status = status;
index 1b8af461b522c65a111983226ba1e704f012ac8c..a7019d1e305814867bb43792ebe55eb00dd7c3fd 100644 (file)
  * Manufacturer: Crucible Technologies
  */
 #define FTDI_CT_COMET_PID      0x8e08
+
+/*
+ * Product: Z3X Box
+ * Manufacturer: Smart GSM Team
+ */
+#define FTDI_Z3X_PID           0x0011
index 1f31e6b4c2518f262a263bbefdddf5fda6ba9cb6..2b01ec8651c296e3f016bf2421040da6f1bbbc98 100644 (file)
@@ -7,7 +7,6 @@
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License version
  *     2 as published by the Free Software Foundation.
- *
  */
 
 #include <linux/kernel.h>
@@ -37,7 +36,6 @@ MODULE_PARM_DESC(product, "User specified USB idProduct");
 
 static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
 
-/* All of the device info needed for the Generic Serial Converter */
 struct usb_serial_driver usb_serial_generic_device = {
        .driver = {
                .owner =        THIS_MODULE,
@@ -66,7 +64,6 @@ int usb_serial_generic_register(void)
        generic_device_ids[0].match_flags =
                USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
 
-       /* register our generic driver with ourselves */
        retval = usb_serial_register_drivers(serial_drivers,
                        "usbserial_generic", generic_device_ids);
 #endif
@@ -76,7 +73,6 @@ int usb_serial_generic_register(void)
 void usb_serial_generic_deregister(void)
 {
 #ifdef CONFIG_USB_SERIAL_GENERIC
-       /* remove our generic driver */
        usb_serial_deregister_drivers(serial_drivers);
 #endif
 }
@@ -86,13 +82,11 @@ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port
        int result = 0;
        unsigned long flags;
 
-       /* clear the throttle flags */
        spin_lock_irqsave(&port->lock, flags);
        port->throttled = 0;
        port->throttle_req = 0;
        spin_unlock_irqrestore(&port->lock, flags);
 
-       /* if we have a bulk endpoint, start reading from it */
        if (port->bulk_in_size)
                result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
 
@@ -127,12 +121,16 @@ int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
 }
 
 /**
- * usb_serial_generic_write_start - kick off an URB write
- * @port:      Pointer to the &struct usb_serial_port data
+ * usb_serial_generic_write_start - start writing buffered data
+ * @port: usb-serial port
+ * @mem_flags: flags to use for memory allocations
+ *
+ * Serialised using USB_SERIAL_WRITE_BUSY flag.
  *
- * Returns zero on success, or a negative errno value
+ * Return: Zero on success or if busy, otherwise a negative errno value.
  */
-static int usb_serial_generic_write_start(struct usb_serial_port *port)
+int usb_serial_generic_write_start(struct usb_serial_port *port,
+                                                       gfp_t mem_flags)
 {
        struct urb *urb;
        int count, result;
@@ -163,7 +161,7 @@ retry:
        spin_unlock_irqrestore(&port->lock, flags);
 
        clear_bit(i, &port->write_urbs_free);
-       result = usb_submit_urb(urb, GFP_ATOMIC);
+       result = usb_submit_urb(urb, mem_flags);
        if (result) {
                dev_err_console(port, "%s - error submitting urb: %d\n",
                                                __func__, result);
@@ -175,34 +173,34 @@ retry:
                clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
                return result;
        }
-
-       /* Try sending off another urb, unless in irq context (in which case
-        * there will be no free urb). */
-       if (!in_irq())
+       /*
+        * Try sending off another urb, unless called from completion handler
+        * (in which case there will be no free urb or no data).
+        */
+       if (mem_flags != GFP_ATOMIC)
                goto retry;
 
        clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_serial_generic_write_start);
 
 /**
- * usb_serial_generic_write - generic write function for serial USB devices
- * @tty:       Pointer to &struct tty_struct for the device
- * @port:      Pointer to the &usb_serial_port structure for the device
- * @buf:       Pointer to the data to write
- * @count:     Number of bytes to write
+ * usb_serial_generic_write - generic write function
+ * @tty: tty for the port
+ * @port: usb-serial port
+ * @buf: data to write
+ * @count: number of bytes to write
  *
- * Returns the number of characters actually written, which may be anything
- * from zero to @count. If an error occurs, it returns the negative errno
- * value.
+ * Return: The number of characters buffered, which may be anything from
+ * zero to @count, or a negative errno value.
  */
 int usb_serial_generic_write(struct tty_struct *tty,
        struct usb_serial_port *port, const unsigned char *buf, int count)
 {
        int result;
 
-       /* only do something if we have a bulk out endpoint */
        if (!port->bulk_out_size)
                return -ENODEV;
 
@@ -210,7 +208,7 @@ int usb_serial_generic_write(struct tty_struct *tty,
                return 0;
 
        count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
-       result = usb_serial_generic_write_start(port);
+       result = usb_serial_generic_write_start(port, GFP_KERNEL);
        if (result)
                return result;
 
@@ -337,10 +335,11 @@ void usb_serial_generic_process_read_urb(struct urb *urb)
 
        if (!urb->actual_length)
                return;
-
-       /* The per character mucking around with sysrq path it too slow for
-          stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases
-          where the USB serial is not a console anyway */
+       /*
+        * The per character mucking around with sysrq path it too slow for
+        * stuff like 3G modems, so shortcircuit it in the 99.9999999% of
+        * cases where the USB serial is not a console anyway.
+        */
        if (!port->port.console || !port->sysrq)
                tty_insert_flip_string(&port->port, ch, urb->actual_length);
        else {
@@ -413,7 +412,7 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb)
                kfifo_reset_out(&port->write_fifo);
                spin_unlock_irqrestore(&port->lock, flags);
        } else {
-               usb_serial_generic_write_start(port);
+               usb_serial_generic_write_start(port, GFP_ATOMIC);
        }
 
        usb_serial_port_softint(port);
@@ -425,8 +424,6 @@ void usb_serial_generic_throttle(struct tty_struct *tty)
        struct usb_serial_port *port = tty->driver_data;
        unsigned long flags;
 
-       /* Set the throttle request flag. It will be picked up
-        * by usb_serial_generic_read_bulk_callback(). */
        spin_lock_irqsave(&port->lock, flags);
        port->throttle_req = 1;
        spin_unlock_irqrestore(&port->lock, flags);
@@ -438,7 +435,6 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty)
        struct usb_serial_port *port = tty->driver_data;
        int was_throttled;
 
-       /* Clear the throttle flags */
        spin_lock_irq(&port->lock);
        was_throttled = port->throttled;
        port->throttled = port->throttle_req = 0;
@@ -558,10 +554,10 @@ int usb_serial_handle_break(struct usb_serial_port *port)
 EXPORT_SYMBOL_GPL(usb_serial_handle_break);
 
 /**
- *     usb_serial_handle_dcd_change - handle a change of carrier detect state
- *     @port: usb_serial_port structure for the open port
- *     @tty: tty_struct structure for the port
- *     @status: new carrier detect status, nonzero if active
+ * usb_serial_handle_dcd_change - handle a change of carrier detect state
+ * @port: usb-serial port
+ * @tty: tty for the port
+ * @status: new carrier detect status, nonzero if active
  */
 void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
                                struct tty_struct *tty, unsigned int status)
@@ -570,6 +566,16 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
 
        dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status);
 
+       if (tty) {
+               struct tty_ldisc *ld = tty_ldisc_ref(tty);
+
+               if (ld) {
+                       if (ld->ops->dcd_change)
+                               ld->ops->dcd_change(tty, status);
+                       tty_ldisc_deref(ld);
+               }
+       }
+
        if (status)
                wake_up_interruptible(&port->open_wait);
        else if (tty && !C_CLOCAL(tty))
@@ -595,7 +601,7 @@ int usb_serial_generic_resume(struct usb_serial *serial)
                }
 
                if (port->bulk_out_size) {
-                       r = usb_serial_generic_write_start(port);
+                       r = usb_serial_generic_write_start(port, GFP_NOIO);
                        if (r < 0)
                                c++;
                }
index fdf953539c62b4943a5ef1c6f93eab52cd53e463..e5bdd987b9e8f7e260fc95c48b142742026999fa 100644 (file)
@@ -1532,7 +1532,11 @@ static int mos7840_tiocmget(struct tty_struct *tty)
                return -ENODEV;
 
        status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
+       if (status != 1)
+               return -EIO;
        status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
+       if (status != 1)
+               return -EIO;
        result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
            | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
            | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
index acaee066b99aa10e5ecf2a8e75cffd31899b6507..c3d94853b4ab7a2dd13d8f6dea0009f55795ba56 100644 (file)
@@ -1376,6 +1376,23 @@ static const struct usb_device_id option_ids[] = {
                .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff),  /* ZTE MF91 */
                .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1545, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1546, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1547, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1565, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1566, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1567, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1589, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1590, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1591, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1592, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1594, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1596, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1598, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1600, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff,
          0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
index bedf8e47713be02dfc80b8a2e3512cb24a455a15..1e6de4cd079d6a2c0e3b4b3c7679d6145bcf6f2c 100644 (file)
@@ -4,11 +4,6 @@
  * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)
  * Copyright (C) 2003 IBM Corp.
  *
- * Copyright (C) 2009, 2013 Frank Schäfer <fschaefer.oss@googlemail.com>
- *  - fixes, improvements and documentation for the baud rate encoding methods
- * Copyright (C) 2013 Reinhard Max <max@suse.de>
- *  - fixes and improvements for the divisor based baud rate encoding method
- *
  * Original driver for 2.2.x by anonymous
  *
  *     This program is free software; you can redistribute it and/or
@@ -134,18 +129,10 @@ MODULE_DEVICE_TABLE(usb, id_table);
 
 
 enum pl2303_type {
-       type_0,         /* H version ? */
-       type_1,         /* H version ? */
-       HX_TA,          /* HX(A) / X(A) / TA version  */ /* TODO: improve */
-       HXD_EA_RA_SA,   /* HXD / EA / RA / SA version */ /* TODO: improve */
-       TB,             /* TB version */
-       HX_CLONE,       /* Cheap and less functional clone of the HX chip */
+       type_0,         /* don't know the difference between type 0 and */
+       type_1,         /* type 1, until someone from prolific tells us... */
+       HX,             /* HX version of the pl2303 chip */
 };
-/*
- * NOTE: don't know the difference between type 0 and type 1,
- * until someone from Prolific tells us...
- * TODO: distinguish between X/HX, TA and HXD, EA, RA, SA variants
- */
 
 struct pl2303_serial_private {
        enum pl2303_type type;
@@ -185,7 +172,6 @@ static int pl2303_startup(struct usb_serial *serial)
 {
        struct pl2303_serial_private *spriv;
        enum pl2303_type type = type_0;
-       char *type_str = "unknown (treating as type_0)";
        unsigned char *buf;
 
        spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
@@ -198,53 +184,15 @@ static int pl2303_startup(struct usb_serial *serial)
                return -ENOMEM;
        }
 
-       if (serial->dev->descriptor.bDeviceClass == 0x02) {
+       if (serial->dev->descriptor.bDeviceClass == 0x02)
                type = type_0;
-               type_str = "type_0";
-       } else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) {
-               /*
-                * NOTE: The bcdDevice version is the only difference between
-                * the device descriptors of the X/HX, HXD, EA, RA, SA, TA, TB
-                */
-               if (le16_to_cpu(serial->dev->descriptor.bcdDevice) == 0x300) {
-                       /* Check if the device is a clone */
-                       pl2303_vendor_read(0x9494, 0, serial, buf);
-                       /*
-                        * NOTE: Not sure if this read is really needed.
-                        * The HX returns 0x00, the clone 0x02, but the Windows
-                        * driver seems to ignore the value and continues.
-                        */
-                       pl2303_vendor_write(0x0606, 0xaa, serial);
-                       pl2303_vendor_read(0x8686, 0, serial, buf);
-                       if (buf[0] != 0xaa) {
-                               type = HX_CLONE;
-                               type_str = "X/HX clone (limited functionality)";
-                       } else {
-                               type = HX_TA;
-                               type_str = "X/HX/TA";
-                       }
-                       pl2303_vendor_write(0x0606, 0x00, serial);
-               } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice)
-                                                                    == 0x400) {
-                       type = HXD_EA_RA_SA;
-                       type_str = "HXD/EA/RA/SA";
-               } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice)
-                                                                    == 0x500) {
-                       type = TB;
-                       type_str = "TB";
-               } else {
-                       dev_info(&serial->interface->dev,
-                                          "unknown/unsupported device type\n");
-                       kfree(spriv);
-                       kfree(buf);
-                       return -ENODEV;
-               }
-       } else if (serial->dev->descriptor.bDeviceClass == 0x00
-                  || serial->dev->descriptor.bDeviceClass == 0xFF) {
+       else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
+               type = HX;
+       else if (serial->dev->descriptor.bDeviceClass == 0x00)
                type = type_1;
-               type_str = "type_1";
-       }
-       dev_dbg(&serial->interface->dev, "device type: %s\n", type_str);
+       else if (serial->dev->descriptor.bDeviceClass == 0xFF)
+               type = type_1;
+       dev_dbg(&serial->interface->dev, "device type: %d\n", type);
 
        spriv->type = type;
        usb_set_serial_data(serial, spriv);
@@ -259,10 +207,10 @@ static int pl2303_startup(struct usb_serial *serial)
        pl2303_vendor_read(0x8383, 0, serial, buf);
        pl2303_vendor_write(0, 1, serial);
        pl2303_vendor_write(1, 0, serial);
-       if (type == type_0 || type == type_1)
-               pl2303_vendor_write(2, 0x24, serial);
-       else
+       if (type == HX)
                pl2303_vendor_write(2, 0x44, serial);
+       else
+               pl2303_vendor_write(2, 0x24, serial);
 
        kfree(buf);
        return 0;
@@ -316,174 +264,65 @@ static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value)
        return retval;
 }
 
-static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type,
-                                                                     u8 buf[4])
+static void pl2303_encode_baudrate(struct tty_struct *tty,
+                                       struct usb_serial_port *port,
+                                       u8 buf[4])
 {
-       /*
-        * NOTE: Only the values defined in baud_sup are supported !
-        * => if unsupported values are set, the PL2303 uses 9600 baud instead
-        * => HX clones just don't work at unsupported baud rates < 115200 baud,
-        *    for baud rates > 115200 they run at 115200 baud
-        */
        const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600,
-                                4800, 7200, 9600, 14400, 19200, 28800, 38400,
-                                57600, 115200, 230400, 460800, 614400, 921600,
-                                1228800, 2457600, 3000000, 6000000, 12000000 };
+                                4800, 7200, 9600, 14400, 19200, 28800, 38400,
+                                57600, 115200, 230400, 460800, 500000, 614400,
+                                921600, 1228800, 2457600, 3000000, 6000000 };
+
+       struct usb_serial *serial = port->serial;
+       struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+       int baud;
+       int i;
+
        /*
-        * NOTE: With the exception of type_0/1 devices, the following
-        * additional baud rates are supported (tested with HX rev. 3A only):
-        * 110*, 56000*, 128000, 134400, 161280, 201600, 256000*, 268800,
-        * 403200, 806400.      (*: not HX and HX clones)
-        *
-        * Maximum values: HXD, TB: 12000000; HX, TA: 6000000;
-        *                 type_0+1: 1228800; RA: 921600; HX clones, SA: 115200
-        *
-        * As long as we are not using this encoding method for anything else
-        * than the type_0+1, HX and HX clone chips, there is no point in
-        * complicating the code to support them.
+        * NOTE: Only the values defined in baud_sup are supported!
+        *       => if unsupported values are set, the PL2303 seems to use
+        *          9600 baud (at least my PL2303X always does)
         */
-       int i;
+       baud = tty_get_baud_rate(tty);
+       dev_dbg(&port->dev, "baud requested = %d\n", baud);
+       if (!baud)
+               return;
 
        /* Set baudrate to nearest supported value */
        for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) {
                if (baud_sup[i] > baud)
                        break;
        }
+
        if (i == ARRAY_SIZE(baud_sup))
                baud = baud_sup[i - 1];
        else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1]))
                baud = baud_sup[i - 1];
        else
                baud = baud_sup[i];
-       /* Respect the chip type specific baud rate limits */
-       /*
-        * FIXME: as long as we don't know how to distinguish between the
-        * HXD, EA, RA, and SA chip variants, allow the max. value of 12M.
-        */
-       if (type == HX_TA)
-               baud = min_t(int, baud, 6000000);
-       else if (type == type_0 || type == type_1)
-               baud = min_t(int, baud, 1228800);
-       else if (type == HX_CLONE)
-               baud = min_t(int, baud, 115200);
-       /* Direct (standard) baud rate encoding method */
-       put_unaligned_le32(baud, buf);
-
-       return baud;
-}
 
-static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type,
-                                                                     u8 buf[4])
-{
-       /*
-        * Divisor based baud rate encoding method
-        *
-        * NOTE: HX clones do NOT support this method.
-        * It's not clear if the type_0/1 chips support it.
-        *
-        * divisor = 12MHz * 32 / baudrate = 2^A * B
-        *
-        * with
-        *
-        * A = buf[1] & 0x0e
-        * B = buf[0]  +  (buf[1] & 0x01) << 8
-        *
-        * Special cases:
-        * => 8 < B < 16: device seems to work not properly
-        * => B <= 8: device uses the max. value B = 512 instead
-        */
-       unsigned int A, B;
+       /* type_0, type_1 only support up to 1228800 baud */
+       if (spriv->type != HX)
+               baud = min_t(int, baud, 1228800);
 
-       /*
-        * NOTE: The Windows driver allows maximum baud rates of 110% of the
-        * specified maximium value.
-        * Quick tests with early (2004) HX (rev. A) chips suggest, that even
-        * higher baud rates (up to the maximum of 24M baud !) are working fine,
-        * but that should really be tested carefully in "real life" scenarios
-        * before removing the upper limit completely.
-        * Baud rates smaller than the specified 75 baud are definitely working
-        * fine.
-        */
-       if (type == type_0 || type == type_1)
-               baud = min_t(int, baud, 1228800 * 1.1);
-       else if (type == HX_TA)
-               baud = min_t(int, baud, 6000000 * 1.1);
-       else if (type == HXD_EA_RA_SA)
-               /* HXD, EA: 12Mbps; RA: 1Mbps; SA: 115200 bps */
-               /*
-                * FIXME: as long as we don't know how to distinguish between
-                * these chip variants, allow the max. of these values
-                */
-               baud = min_t(int, baud, 12000000 * 1.1);
-       else if (type == TB)
-               baud = min_t(int, baud, 12000000 * 1.1);
-       /* Determine factors A and B */
-       A = 0;
-       B = 12000000 * 32 / baud;  /* 12MHz */
-       B <<= 1; /* Add one bit for rounding */
-       while (B > (512 << 1) && A <= 14) {
-               A += 2;
-               B >>= 2;
-       }
-       if (A > 14) { /* max. divisor = min. baudrate reached */
-               A = 14;
-               B = 512;
-               /* => ~45.78 baud */
+       if (baud <= 115200) {
+               put_unaligned_le32(baud, buf);
        } else {
-               B = (B + 1) >> 1; /* Round the last bit */
-       }
-       /* Handle special cases */
-       if (B == 512)
-               B = 0; /* also: 1 to 8 */
-       else if (B < 16)
                /*
-                * NOTE: With the current algorithm this happens
-                * only for A=0 and means that the min. divisor
-                * (respectively: the max. baudrate) is reached.
+                * Apparently the formula for higher speeds is:
+                * baudrate = 12M * 32 / (2^buf[1]) / buf[0]
                 */
-               B = 16;         /* => 24 MBaud */
-       /* Encode the baud rate */
-       buf[3] = 0x80;     /* Select divisor encoding method */
-       buf[2] = 0;
-       buf[1] = (A & 0x0e);            /* A */
-       buf[1] |= ((B & 0x100) >> 8);   /* MSB of B */
-       buf[0] = B & 0xff;              /* 8 LSBs of B */
-       /* Calculate the actual/resulting baud rate */
-       if (B <= 8)
-               B = 512;
-       baud = 12000000 * 32 / ((1 << A) * B);
-
-       return baud;
-}
-
-static void pl2303_encode_baudrate(struct tty_struct *tty,
-                                       struct usb_serial_port *port,
-                                       enum pl2303_type type,
-                                       u8 buf[4])
-{
-       int baud;
+               unsigned tmp = 12000000 * 32 / baud;
+               buf[3] = 0x80;
+               buf[2] = 0;
+               buf[1] = (tmp >= 256);
+               while (tmp >= 256) {
+                       tmp >>= 2;
+                       buf[1] <<= 1;
+               }
+               buf[0] = tmp;
+       }
 
-       baud = tty_get_baud_rate(tty);
-       dev_dbg(&port->dev, "baud requested = %d\n", baud);
-       if (!baud)
-               return;
-       /*
-        * There are two methods for setting/encoding the baud rate
-        * 1) Direct method: encodes the baud rate value directly
-        *    => supported by all chip types
-        * 2) Divisor based method: encodes a divisor to a base value (12MHz*32)
-        *    => not supported by HX clones (and likely type_0/1 chips)
-        *
-        * NOTE: Although the divisor based baud rate encoding method is much
-        * more flexible, some of the standard baud rate values can not be
-        * realized exactly. But the difference is very small (max. 0.2%) and
-        * the device likely uses the same baud rate generator for both methods
-        * so that there is likley no difference.
-        */
-       if (type == type_0 || type == type_1 || type == HX_CLONE)
-               baud = pl2303_baudrate_encode_direct(baud, type, buf);
-       else
-               baud = pl2303_baudrate_encode_divisor(baud, type, buf);
        /* Save resulting baud rate */
        tty_encode_baud_rate(tty, baud, baud);
        dev_dbg(&port->dev, "baud set = %d\n", baud);
@@ -540,8 +379,8 @@ static void pl2303_set_termios(struct tty_struct *tty,
                dev_dbg(&port->dev, "data bits = %d\n", buf[6]);
        }
 
-       /* For reference:   buf[0]:buf[3] baud rate value */
-       pl2303_encode_baudrate(tty, port, spriv->type, buf);
+       /* For reference buf[0]:buf[3] baud rate value */
+       pl2303_encode_baudrate(tty, port, &buf[0]);
 
        /* For reference buf[4]=0 is 1 stop bits */
        /* For reference buf[4]=1 is 1.5 stop bits */
@@ -618,10 +457,10 @@ static void pl2303_set_termios(struct tty_struct *tty,
        dev_dbg(&port->dev, "0xa1:0x21:0:0  %d - %7ph\n", i, buf);
 
        if (C_CRTSCTS(tty)) {
-               if (spriv->type == type_0 || spriv->type == type_1)
-                       pl2303_vendor_write(0x0, 0x41, serial);
-               else
+               if (spriv->type == HX)
                        pl2303_vendor_write(0x0, 0x61, serial);
+               else
+                       pl2303_vendor_write(0x0, 0x41, serial);
        } else {
                pl2303_vendor_write(0x0, 0x0, serial);
        }
@@ -658,7 +497,7 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
        struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
        int result;
 
-       if (spriv->type == type_0 || spriv->type == type_1) {
+       if (spriv->type != HX) {
                usb_clear_halt(serial->dev, port->write_urb->pipe);
                usb_clear_halt(serial->dev, port->read_urb->pipe);
        } else {
@@ -833,7 +672,6 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state)
        result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                                 BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
                                 0, NULL, 0, 100);
-       /* NOTE: HX clones don't support sending breaks, -EPIPE is returned */
        if (result)
                dev_err(&port->dev, "error sending break = %d\n", result);
 }
index 7f78f300f8fbe02674aa7f1f0846d8399c3be0f8..f06ed82e63d139fa2d25efd1ef2ac7cf5545e995 100644 (file)
@@ -208,9 +208,9 @@ static int cbaf_check(struct cbaf *cbaf)
                                ar_name = "ASSOCIATE";
                                ar_assoc = 1;
                                break;
-                       };
+                       }
                        break;
-               };
+               }
 
                dev_dbg(dev, "Association request #%02u: 0x%04x/%04x "
                         "(%zu bytes): %s\n",
@@ -623,6 +623,8 @@ static int cbaf_probe(struct usb_interface *iface,
 
 error_create_group:
 error_check:
+       usb_put_intf(iface);
+       usb_put_dev(cbaf->usb_dev);
        kfree(cbaf->buffer);
 error_kmalloc_buffer:
        kfree(cbaf);
@@ -637,6 +639,7 @@ static void cbaf_disconnect(struct usb_interface *iface)
        sysfs_remove_group(&dev->kobj, &cbaf_dev_attr_group);
        usb_set_intfdata(iface, NULL);
        usb_put_intf(iface);
+       usb_put_dev(cbaf->usb_dev);
        kfree(cbaf->buffer);
        /* paranoia: clean up crypto keys */
        kzfree(cbaf);
index 33a12788f9ca063c8aae13ce28d9b978f27a372c..e538b72c4e3af2d09153614952e12101eac03520 100644 (file)
@@ -973,7 +973,7 @@ int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
        default:
                WARN_ON(1);
                result = NOTIFY_BAD;
-       };
+       }
        return result;
 }
 
index a09b65ebd9bb712d02060e760ff2c349bcdc2e77..368360f9a93ae1283780c3b30e230c6efd630b38 100644 (file)
@@ -33,7 +33,8 @@
  * wa->usb_dev and wa->usb_iface initialized and refcounted,
  * wa->wa_descr initialized.
  */
-int wa_create(struct wahc *wa, struct usb_interface *iface)
+int wa_create(struct wahc *wa, struct usb_interface *iface,
+       kernel_ulong_t quirks)
 {
        int result;
        struct device *dev = &iface->dev;
@@ -41,14 +42,15 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
        result = wa_rpipes_create(wa);
        if (result < 0)
                goto error_rpipes_create;
+       wa->quirks = quirks;
        /* Fill up Data Transfer EP pointers */
        wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
        wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
-       wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd);
-       wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL);
-       if (wa->xfer_result == NULL) {
+       wa->dti_buf_size = usb_endpoint_maxp(wa->dti_epd);
+       wa->dti_buf = kmalloc(wa->dti_buf_size, GFP_KERNEL);
+       if (wa->dti_buf == NULL) {
                result = -ENOMEM;
-               goto error_xfer_result_alloc;
+               goto error_dti_buf_alloc;
        }
        result = wa_nep_create(wa, iface);
        if (result < 0) {
@@ -59,8 +61,8 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
        return 0;
 
 error_nep_create:
-       kfree(wa->xfer_result);
-error_xfer_result_alloc:
+       kfree(wa->dti_buf);
+error_dti_buf_alloc:
        wa_rpipes_destroy(wa);
 error_rpipes_create:
        return result;
@@ -76,7 +78,7 @@ void __wa_destroy(struct wahc *wa)
                usb_kill_urb(wa->buf_in_urb);
                usb_put_urb(wa->buf_in_urb);
        }
-       kfree(wa->xfer_result);
+       kfree(wa->dti_buf);
        wa_nep_destroy(wa);
        wa_rpipes_destroy(wa);
 }
index cf250c21e946bcab938baafc6ca3f48ca2f1a2d3..e614f02f0cf2cef4478f0a4a8aa1fc1ddecb7175 100644 (file)
@@ -117,11 +117,25 @@ struct wa_rpipe {
        struct wahc *wa;
        spinlock_t seg_lock;
        struct list_head seg_list;
+       struct list_head list_node;
        atomic_t segs_available;
        u8 buffer[1];   /* For reads/writes on USB */
 };
 
 
+enum wa_dti_state {
+       WA_DTI_TRANSFER_RESULT_PENDING,
+       WA_DTI_ISOC_PACKET_STATUS_PENDING
+};
+
+enum wa_quirks {
+       /*
+        * The Alereon HWA expects the data frames in isochronous transfer
+        * requests to be concatenated and not sent as separate packets.
+        */
+       WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC      = 0x01,
+};
+
 /**
  * Instance of a HWA Host Controller
  *
@@ -178,14 +192,26 @@ struct wahc {
 
        u16 rpipes;
        unsigned long *rpipe_bm;        /* rpipe usage bitmap */
-       spinlock_t rpipe_bm_lock;       /* protect rpipe_bm */
+       struct list_head rpipe_delayed_list;    /* delayed RPIPES. */
+       spinlock_t rpipe_lock;  /* protect rpipe_bm and delayed list */
        struct mutex rpipe_mutex;       /* assigning resources to endpoints */
 
+       /*
+        * dti_state is used to track the state of the dti_urb.  When dti_state
+        * is WA_DTI_ISOC_PACKET_STATUS_PENDING, dti_isoc_xfer_in_progress and
+        * dti_isoc_xfer_seg identify which xfer the incoming isoc packet status
+        * refers to.
+        */
+       enum wa_dti_state dti_state;
+       u32 dti_isoc_xfer_in_progress;
+       u8  dti_isoc_xfer_seg;
        struct urb *dti_urb;            /* URB for reading xfer results */
        struct urb *buf_in_urb;         /* URB for reading data in */
        struct edc dti_edc;             /* DTI error density counter */
-       struct wa_xfer_result *xfer_result; /* real size = dti_ep maxpktsize */
-       size_t xfer_result_size;
+       void *dti_buf;
+       size_t dti_buf_size;
+
+       unsigned long dto_in_use;       /* protect dto endoint serialization. */
 
        s32 status;                     /* For reading status */
 
@@ -200,10 +226,13 @@ struct wahc {
        struct work_struct xfer_enqueue_work;
        struct work_struct xfer_error_work;
        atomic_t xfer_id_count;
+
+       kernel_ulong_t  quirks;
 };
 
 
-extern int wa_create(struct wahc *wa, struct usb_interface *iface);
+extern int wa_create(struct wahc *wa, struct usb_interface *iface,
+       kernel_ulong_t);
 extern void __wa_destroy(struct wahc *wa);
 void wa_reset_all(struct wahc *wa);
 
@@ -239,7 +268,8 @@ static inline void wa_nep_disarm(struct wahc *wa)
 /* RPipes */
 static inline void wa_rpipe_init(struct wahc *wa)
 {
-       spin_lock_init(&wa->rpipe_bm_lock);
+       INIT_LIST_HEAD(&wa->rpipe_delayed_list);
+       spin_lock_init(&wa->rpipe_lock);
        mutex_init(&wa->rpipe_mutex);
 }
 
@@ -247,6 +277,7 @@ static inline void wa_init(struct wahc *wa)
 {
        edc_init(&wa->nep_edc);
        atomic_set(&wa->notifs_queued, 0);
+       wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
        wa_rpipe_init(wa);
        edc_init(&wa->dti_edc);
        INIT_LIST_HEAD(&wa->xfer_list);
@@ -255,6 +286,7 @@ static inline void wa_init(struct wahc *wa)
        spin_lock_init(&wa->xfer_list_lock);
        INIT_WORK(&wa->xfer_enqueue_work, wa_urb_enqueue_run);
        INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run);
+       wa->dto_in_use = 0;
        atomic_set(&wa->xfer_id_count, 1);
 }
 
index fd4f1ce6256ac7a60fd2a9d5745e88b6f3c98d63..b48e74cc54d71b92e1ef9c0b918ea5cd9404c1e9 100644 (file)
@@ -143,17 +143,18 @@ static void rpipe_init(struct wa_rpipe *rpipe)
        kref_init(&rpipe->refcnt);
        spin_lock_init(&rpipe->seg_lock);
        INIT_LIST_HEAD(&rpipe->seg_list);
+       INIT_LIST_HEAD(&rpipe->list_node);
 }
 
 static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+       spin_lock_irqsave(&wa->rpipe_lock, flags);
        rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx);
        if (rpipe_idx < wa->rpipes)
                set_bit(rpipe_idx, wa->rpipe_bm);
-       spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+       spin_unlock_irqrestore(&wa->rpipe_lock, flags);
 
        return rpipe_idx;
 }
@@ -162,9 +163,9 @@ static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+       spin_lock_irqsave(&wa->rpipe_lock, flags);
        clear_bit(rpipe_idx, wa->rpipe_bm);
-       spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+       spin_unlock_irqrestore(&wa->rpipe_lock, flags);
 }
 
 void rpipe_destroy(struct kref *_rpipe)
@@ -333,7 +334,10 @@ static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa,
        /* FIXME: compute so seg_size > ep->maxpktsize */
        rpipe->descr.wBlocks = cpu_to_le16(16);         /* given */
        /* ep0 maxpktsize is 0x200 (WUSB1.0[4.8.1]) */
-       rpipe->descr.wMaxPacketSize = cpu_to_le16(ep->desc.wMaxPacketSize);
+       if (usb_endpoint_xfer_isoc(&ep->desc))
+               rpipe->descr.wMaxPacketSize = epcd->wOverTheAirPacketSize;
+       else
+               rpipe->descr.wMaxPacketSize = ep->desc.wMaxPacketSize;
 
        rpipe->descr.hwa_bMaxBurst = max(min_t(unsigned int,
                                epcd->bMaxBurst, 16U), 1U);
@@ -361,8 +365,10 @@ static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa,
                        epcd->bMaxSequence, 32U), 2U);
        rpipe->descr.bMaxDataSequence = epcd_max_sequence - 1;
        rpipe->descr.bInterval = ep->desc.bInterval;
-       /* FIXME: bOverTheAirInterval */
-       rpipe->descr.bOverTheAirInterval = 0;   /* 0 if not isoc */
+       if (usb_endpoint_xfer_isoc(&ep->desc))
+               rpipe->descr.bOverTheAirInterval = epcd->bOverTheAirInterval;
+       else
+               rpipe->descr.bOverTheAirInterval = 0;   /* 0 if not isoc */
        /* FIXME: xmit power & preamble blah blah */
        rpipe->descr.bmAttribute = (ep->desc.bmAttributes &
                                        USB_ENDPOINT_XFERTYPE_MASK);
@@ -477,7 +483,7 @@ error:
  */
 int wa_rpipes_create(struct wahc *wa)
 {
-       wa->rpipes = wa->wa_descr->wNumRPipes;
+       wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes);
        wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long),
                               GFP_KERNEL);
        if (wa->rpipe_bm == NULL)
index 6ad02f57c366706e85ab4e3d03fa2d6cdc006a79..ed5abe87b0496f1329f3eefada1b636a9151fb70 100644 (file)
@@ -91,7 +91,8 @@
 #include "wusbhc.h"
 
 enum {
-       WA_SEGS_MAX = 255,
+       /* [WUSB] section 8.3.3 allocates 7 bits for the segment index. */
+       WA_SEGS_MAX = 128,
 };
 
 enum wa_seg_status {
@@ -107,6 +108,7 @@ enum wa_seg_status {
 };
 
 static void wa_xfer_delayed_run(struct wa_rpipe *);
+static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting);
 
 /*
  * Life cycle governed by 'struct urb' (the refcount of the struct is
@@ -114,24 +116,27 @@ static void wa_xfer_delayed_run(struct wa_rpipe *);
  * struct).
  */
 struct wa_seg {
-       struct urb urb;
-       struct urb *dto_urb;            /* for data output? */
+       struct urb tr_urb;              /* transfer request urb. */
+       struct urb *isoc_pack_desc_urb; /* for isoc packet descriptor. */
+       struct urb *dto_urb;            /* for data output. */
        struct list_head list_node;     /* for rpipe->req_list */
        struct wa_xfer *xfer;           /* out xfer */
        u8 index;                       /* which segment we are */
+       int isoc_frame_count;   /* number of isoc frames in this segment. */
+       int isoc_frame_offset;  /* starting frame offset in the xfer URB. */
+       int isoc_size;  /* size of all isoc frames sent by this seg. */
        enum wa_seg_status status;
        ssize_t result;                 /* bytes xfered or error */
        struct wa_xfer_hdr xfer_hdr;
-       u8 xfer_extra[];                /* xtra space for xfer_hdr_ctl */
 };
 
 static inline void wa_seg_init(struct wa_seg *seg)
 {
-       usb_init_urb(&seg->urb);
+       usb_init_urb(&seg->tr_urb);
 
        /* set the remaining memory to 0. */
-       memset(((void *)seg) + sizeof(seg->urb), 0,
-               sizeof(*seg) - sizeof(seg->urb));
+       memset(((void *)seg) + sizeof(seg->tr_urb), 0,
+               sizeof(*seg) - sizeof(seg->tr_urb));
 }
 
 /*
@@ -153,12 +158,17 @@ struct wa_xfer {
        unsigned is_dma:1;
        size_t seg_size;
        int result;
+       /* Isoc frame that the current transfer buffer corresponds to. */
+       int dto_isoc_frame_index;
 
        gfp_t gfp;                      /* allocation mask */
 
        struct wusb_dev *wusb_dev;      /* for activity timestamps */
 };
 
+static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer,
+       struct wa_seg *seg, int curr_iso_frame);
+
 static inline void wa_xfer_init(struct wa_xfer *xfer)
 {
        kref_init(&xfer->refcnt);
@@ -169,7 +179,7 @@ static inline void wa_xfer_init(struct wa_xfer *xfer)
 /*
  * Destroy a transfer structure
  *
- * Note that freeing xfer->seg[cnt]->urb will free the containing
+ * Note that freeing xfer->seg[cnt]->tr_urb will free the containing
  * xfer->seg[cnt] memory that was allocated by __wa_xfer_setup_segs.
  */
 static void wa_xfer_destroy(struct kref *_xfer)
@@ -178,9 +188,17 @@ static void wa_xfer_destroy(struct kref *_xfer)
        if (xfer->seg) {
                unsigned cnt;
                for (cnt = 0; cnt < xfer->segs; cnt++) {
-                       usb_free_urb(xfer->seg[cnt]->dto_urb);
-                       usb_free_urb(&xfer->seg[cnt]->urb);
+                       struct wa_seg *seg = xfer->seg[cnt];
+                       if (seg) {
+                               usb_free_urb(seg->isoc_pack_desc_urb);
+                               if (seg->dto_urb) {
+                                       kfree(seg->dto_urb->sg);
+                                       usb_free_urb(seg->dto_urb);
+                               }
+                               usb_free_urb(&seg->tr_urb);
+                       }
                }
+               kfree(xfer->seg);
        }
        kfree(xfer);
 }
@@ -195,6 +213,59 @@ static void wa_xfer_put(struct wa_xfer *xfer)
        kref_put(&xfer->refcnt, wa_xfer_destroy);
 }
 
+/*
+ * Try to get exclusive access to the DTO endpoint resource.  Return true
+ * if successful.
+ */
+static inline int __wa_dto_try_get(struct wahc *wa)
+{
+       return (test_and_set_bit(0, &wa->dto_in_use) == 0);
+}
+
+/* Release the DTO endpoint resource. */
+static inline void __wa_dto_put(struct wahc *wa)
+{
+       clear_bit_unlock(0, &wa->dto_in_use);
+}
+
+/* Service RPIPEs that are waiting on the DTO resource. */
+static void wa_check_for_delayed_rpipes(struct wahc *wa)
+{
+       unsigned long flags;
+       int dto_waiting = 0;
+       struct wa_rpipe *rpipe;
+
+       spin_lock_irqsave(&wa->rpipe_lock, flags);
+       while (!list_empty(&wa->rpipe_delayed_list) && !dto_waiting) {
+               rpipe = list_first_entry(&wa->rpipe_delayed_list,
+                               struct wa_rpipe, list_node);
+               __wa_xfer_delayed_run(rpipe, &dto_waiting);
+               /* remove this RPIPE from the list if it is not waiting. */
+               if (!dto_waiting) {
+                       pr_debug("%s: RPIPE %d serviced and removed from delayed list.\n",
+                               __func__,
+                               le16_to_cpu(rpipe->descr.wRPipeIndex));
+                       list_del_init(&rpipe->list_node);
+               }
+       }
+       spin_unlock_irqrestore(&wa->rpipe_lock, flags);
+}
+
+/* add this RPIPE to the end of the delayed RPIPE list. */
+static void wa_add_delayed_rpipe(struct wahc *wa, struct wa_rpipe *rpipe)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&wa->rpipe_lock, flags);
+       /* add rpipe to the list if it is not already on it. */
+       if (list_empty(&rpipe->list_node)) {
+               pr_debug("%s: adding RPIPE %d to the delayed list.\n",
+                       __func__, le16_to_cpu(rpipe->descr.wRPipeIndex));
+               list_add_tail(&rpipe->list_node, &wa->rpipe_delayed_list);
+       }
+       spin_unlock_irqrestore(&wa->rpipe_lock, flags);
+}
+
 /*
  * xfer is referenced
  *
@@ -231,6 +302,31 @@ static void wa_xfer_completion(struct wa_xfer *xfer)
        wa_xfer_giveback(xfer);
 }
 
+/*
+ * Initialize a transfer's ID
+ *
+ * We need to use a sequential number; if we use the pointer or the
+ * hash of the pointer, it can repeat over sequential transfers and
+ * then it will confuse the HWA....wonder why in hell they put a 32
+ * bit handle in there then.
+ */
+static void wa_xfer_id_init(struct wa_xfer *xfer)
+{
+       xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count);
+}
+
+/* Return the xfer's ID. */
+static inline u32 wa_xfer_id(struct wa_xfer *xfer)
+{
+       return xfer->id;
+}
+
+/* Return the xfer's ID in transport format (little endian). */
+static inline __le32 wa_xfer_id_le32(struct wa_xfer *xfer)
+{
+       return cpu_to_le32(xfer->id);
+}
+
 /*
  * If transfer is done, wrap it up and return true
  *
@@ -253,33 +349,37 @@ static unsigned __wa_xfer_is_done(struct wa_xfer *xfer)
                switch (seg->status) {
                case WA_SEG_DONE:
                        if (found_short && seg->result > 0) {
-                               dev_dbg(dev, "xfer %p#%u: bad short segments (%zu)\n",
-                                       xfer, cnt, seg->result);
+                               dev_dbg(dev, "xfer %p ID %08X#%u: bad short segments (%zu)\n",
+                                       xfer, wa_xfer_id(xfer), cnt,
+                                       seg->result);
                                urb->status = -EINVAL;
                                goto out;
                        }
                        urb->actual_length += seg->result;
-                       if (seg->result < xfer->seg_size
+                       if (!(usb_pipeisoc(xfer->urb->pipe))
+                               && seg->result < xfer->seg_size
                            && cnt != xfer->segs-1)
                                found_short = 1;
-                       dev_dbg(dev, "xfer %p#%u: DONE short %d "
+                       dev_dbg(dev, "xfer %p ID %08X#%u: DONE short %d "
                                "result %zu urb->actual_length %d\n",
-                               xfer, seg->index, found_short, seg->result,
-                               urb->actual_length);
+                               xfer, wa_xfer_id(xfer), seg->index, found_short,
+                               seg->result, urb->actual_length);
                        break;
                case WA_SEG_ERROR:
                        xfer->result = seg->result;
-                       dev_dbg(dev, "xfer %p#%u: ERROR result %zu\n",
-                               xfer, seg->index, seg->result);
+                       dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zu(0x%08zX)\n",
+                               xfer, wa_xfer_id(xfer), seg->index, seg->result,
+                               seg->result);
                        goto out;
                case WA_SEG_ABORTED:
-                       dev_dbg(dev, "xfer %p#%u ABORTED: result %d\n",
-                               xfer, seg->index, urb->status);
+                       dev_dbg(dev, "xfer %p ID %08X#%u ABORTED: result %d\n",
+                               xfer, wa_xfer_id(xfer), seg->index,
+                               urb->status);
                        xfer->result = urb->status;
                        goto out;
                default:
-                       dev_warn(dev, "xfer %p#%u: is_done bad state %d\n",
-                                xfer, cnt, seg->status);
+                       dev_warn(dev, "xfer %p ID %08X#%u: is_done bad state %d\n",
+                                xfer, wa_xfer_id(xfer), cnt, seg->status);
                        xfer->result = -EINVAL;
                        goto out;
                }
@@ -289,29 +389,6 @@ out:
        return result;
 }
 
-/*
- * Initialize a transfer's ID
- *
- * We need to use a sequential number; if we use the pointer or the
- * hash of the pointer, it can repeat over sequential transfers and
- * then it will confuse the HWA....wonder why in hell they put a 32
- * bit handle in there then.
- */
-static void wa_xfer_id_init(struct wa_xfer *xfer)
-{
-       xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count);
-}
-
-/*
- * Return the xfer's ID associated with xfer
- *
- * Need to generate a
- */
-static u32 wa_xfer_id(struct wa_xfer *xfer)
-{
-       return xfer->id;
-}
-
 /*
  * Search for a transfer list ID on the HCD's URB list
  *
@@ -356,15 +433,11 @@ static void __wa_xfer_abort_cb(struct urb *urb)
  *
  * The callback (see above) does nothing but freeing up the data by
  * putting the URB. Because the URB is allocated at the head of the
- * struct, the whole space we allocated is kfreed.
- *
- * We'll get an 'aborted transaction' xfer result on DTI, that'll
- * politely ignore because at this point the transaction has been
- * marked as aborted already.
+ * struct, the whole space we allocated is kfreed. *
  */
-static void __wa_xfer_abort(struct wa_xfer *xfer)
+static int __wa_xfer_abort(struct wa_xfer *xfer)
 {
-       int result;
+       int result = -ENOMEM;
        struct device *dev = &xfer->wa->usb_iface->dev;
        struct wa_xfer_abort_buffer *b;
        struct wa_rpipe *rpipe = xfer->ep->hcpriv;
@@ -375,7 +448,7 @@ static void __wa_xfer_abort(struct wa_xfer *xfer)
        b->cmd.bLength =  sizeof(b->cmd);
        b->cmd.bRequestType = WA_XFER_ABORT;
        b->cmd.wRPipe = rpipe->descr.wRPipeIndex;
-       b->cmd.dwTransferID = wa_xfer_id(xfer);
+       b->cmd.dwTransferID = wa_xfer_id_le32(xfer);
 
        usb_init_urb(&b->urb);
        usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev,
@@ -385,7 +458,7 @@ static void __wa_xfer_abort(struct wa_xfer *xfer)
        result = usb_submit_urb(&b->urb, GFP_ATOMIC);
        if (result < 0)
                goto error_submit;
-       return;                         /* callback frees! */
+       return result;                          /* callback frees! */
 
 
 error_submit:
@@ -394,10 +467,51 @@ error_submit:
                        xfer, result);
        kfree(b);
 error_kmalloc:
-       return;
+       return result;
 
 }
 
+/*
+ * Calculate the number of isoc frames starting from isoc_frame_offset
+ * that will fit a in transfer segment.
+ */
+static int __wa_seg_calculate_isoc_frame_count(struct wa_xfer *xfer,
+       int isoc_frame_offset, int *total_size)
+{
+       int segment_size = 0, frame_count = 0;
+       int index = isoc_frame_offset;
+       struct usb_iso_packet_descriptor *iso_frame_desc =
+               xfer->urb->iso_frame_desc;
+
+       while ((index < xfer->urb->number_of_packets)
+               && ((segment_size + iso_frame_desc[index].length)
+                               <= xfer->seg_size)) {
+               /*
+                * For Alereon HWA devices, only include an isoc frame in a
+                * segment if it is physically contiguous with the previous
+                * frame.  This is required because those devices expect
+                * the isoc frames to be sent as a single USB transaction as
+                * opposed to one transaction per frame with standard HWA.
+                */
+               if ((xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC)
+                       && (index > isoc_frame_offset)
+                       && ((iso_frame_desc[index - 1].offset +
+                               iso_frame_desc[index - 1].length) !=
+                               iso_frame_desc[index].offset))
+                       break;
+
+               /* this frame fits. count it. */
+               ++frame_count;
+               segment_size += iso_frame_desc[index].length;
+
+               /* move to the next isoc frame. */
+               ++index;
+       }
+
+       *total_size = segment_size;
+       return frame_count;
+}
+
 /*
  *
  * @returns < 0 on error, transfer segment request size if ok
@@ -422,43 +536,92 @@ static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer,
                result = sizeof(struct wa_xfer_bi);
                break;
        case USB_ENDPOINT_XFER_ISOC:
-               dev_err(dev, "FIXME: ISOC not implemented\n");
-               result = -ENOSYS;
-               goto error;
+               if (usb_pipeout(urb->pipe)) {
+                       *pxfer_type = WA_XFER_TYPE_ISO;
+                       result = sizeof(struct wa_xfer_hwaiso);
+               } else {
+                       dev_err(dev, "FIXME: ISOC IN not implemented\n");
+                       result = -ENOSYS;
+                       goto error;
+               }
+               break;
        default:
                /* never happens */
                BUG();
                result = -EINVAL;       /* shut gcc up */
-       };
+       }
        xfer->is_inbound = urb->pipe & USB_DIR_IN ? 1 : 0;
        xfer->is_dma = urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? 1 : 0;
-       xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks)
-               * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1);
-       /* Compute the segment size and make sure it is a multiple of
-        * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of
-        * a check (FIXME) */
+
        maxpktsize = le16_to_cpu(rpipe->descr.wMaxPacketSize);
-       if (xfer->seg_size < maxpktsize) {
-               dev_err(dev, "HW BUG? seg_size %zu smaller than maxpktsize "
-                       "%zu\n", xfer->seg_size, maxpktsize);
-               result = -EINVAL;
-               goto error;
+       if ((rpipe->descr.bmAttribute & 0x3) == USB_ENDPOINT_XFER_ISOC) {
+               int index = 0;
+
+               xfer->seg_size = maxpktsize;
+               xfer->segs = 0;
+               /*
+                * loop over urb->number_of_packets to determine how many
+                * xfer segments will be needed to send the isoc frames.
+                */
+               while (index < urb->number_of_packets) {
+                       int seg_size; /* don't care. */
+                       index += __wa_seg_calculate_isoc_frame_count(xfer,
+                                       index, &seg_size);
+                       ++xfer->segs;
+               }
+       } else {
+               xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks)
+                       * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1);
+               /* Compute the segment size and make sure it is a multiple of
+                * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of
+                * a check (FIXME) */
+               if (xfer->seg_size < maxpktsize) {
+                       dev_err(dev,
+                               "HW BUG? seg_size %zu smaller than maxpktsize %zu\n",
+                               xfer->seg_size, maxpktsize);
+                       result = -EINVAL;
+                       goto error;
+               }
+               xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize;
+               xfer->segs = DIV_ROUND_UP(urb->transfer_buffer_length,
+                                               xfer->seg_size);
+               if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL)
+                       xfer->segs = 1;
        }
-       xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize;
-       xfer->segs = DIV_ROUND_UP(urb->transfer_buffer_length, xfer->seg_size);
-       if (xfer->segs >= WA_SEGS_MAX) {
-               dev_err(dev, "BUG? ops, number of segments %d bigger than %d\n",
-                       (int)(urb->transfer_buffer_length / xfer->seg_size),
+
+       if (xfer->segs > WA_SEGS_MAX) {
+               dev_err(dev, "BUG? oops, number of segments %zu bigger than %d\n",
+                       (urb->transfer_buffer_length/xfer->seg_size),
                        WA_SEGS_MAX);
                result = -EINVAL;
                goto error;
        }
-       if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL)
-               xfer->segs = 1;
 error:
        return result;
 }
 
+static void __wa_setup_isoc_packet_descr(
+               struct wa_xfer_packet_info_hwaiso *packet_desc,
+               struct wa_xfer *xfer,
+               struct wa_seg *seg) {
+       struct usb_iso_packet_descriptor *iso_frame_desc =
+               xfer->urb->iso_frame_desc;
+       int frame_index;
+
+       /* populate isoc packet descriptor. */
+       packet_desc->bPacketType = WA_XFER_ISO_PACKET_INFO;
+       packet_desc->wLength = cpu_to_le16(sizeof(*packet_desc) +
+               (sizeof(packet_desc->PacketLength[0]) *
+                       seg->isoc_frame_count));
+       for (frame_index = 0; frame_index < seg->isoc_frame_count;
+               ++frame_index) {
+               int offset_index = frame_index + seg->isoc_frame_offset;
+               packet_desc->PacketLength[frame_index] =
+                       cpu_to_le16(iso_frame_desc[offset_index].length);
+       }
+}
+
+
 /* Fill in the common request header and xfer-type specific data. */
 static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
                                 struct wa_xfer_hdr *xfer_hdr0,
@@ -466,12 +629,13 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
                                 size_t xfer_hdr_size)
 {
        struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+       struct wa_seg *seg = xfer->seg[0];
 
-       xfer_hdr0 = &xfer->seg[0]->xfer_hdr;
+       xfer_hdr0 = &seg->xfer_hdr;
        xfer_hdr0->bLength = xfer_hdr_size;
        xfer_hdr0->bRequestType = xfer_type;
        xfer_hdr0->wRPipe = rpipe->descr.wRPipeIndex;
-       xfer_hdr0->dwTransferID = wa_xfer_id(xfer);
+       xfer_hdr0->dwTransferID = wa_xfer_id_le32(xfer);
        xfer_hdr0->bTransferSegment = 0;
        switch (xfer_type) {
        case WA_XFER_TYPE_CTL: {
@@ -484,8 +648,18 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
        }
        case WA_XFER_TYPE_BI:
                break;
-       case WA_XFER_TYPE_ISO:
-               printk(KERN_ERR "FIXME: ISOC not implemented\n");
+       case WA_XFER_TYPE_ISO: {
+               struct wa_xfer_hwaiso *xfer_iso =
+                       container_of(xfer_hdr0, struct wa_xfer_hwaiso, hdr);
+               struct wa_xfer_packet_info_hwaiso *packet_desc =
+                       ((void *)xfer_iso) + xfer_hdr_size;
+
+               /* populate the isoc section of the transfer request. */
+               xfer_iso->dwNumOfPackets = cpu_to_le32(seg->isoc_frame_count);
+               /* populate isoc packet descriptor. */
+               __wa_setup_isoc_packet_descr(packet_desc, xfer, seg);
+               break;
+       }
        default:
                BUG();
        };
@@ -494,16 +668,149 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
 /*
  * Callback for the OUT data phase of the segment request
  *
- * Check wa_seg_cb(); most comments also apply here because this
+ * Check wa_seg_tr_cb(); most comments also apply here because this
  * function does almost the same thing and they work closely
  * together.
  *
  * If the seg request has failed but this DTO phase has succeeded,
- * wa_seg_cb() has already failed the segment and moved the
+ * wa_seg_tr_cb() has already failed the segment and moved the
  * status to WA_SEG_ERROR, so this will go through 'case 0' and
  * effectively do nothing.
  */
 static void wa_seg_dto_cb(struct urb *urb)
+{
+       struct wa_seg *seg = urb->context;
+       struct wa_xfer *xfer = seg->xfer;
+       struct wahc *wa;
+       struct device *dev;
+       struct wa_rpipe *rpipe;
+       unsigned long flags;
+       unsigned rpipe_ready = 0;
+       int data_send_done = 1, release_dto = 0, holding_dto = 0;
+       u8 done = 0;
+       int result;
+
+       /* free the sg if it was used. */
+       kfree(urb->sg);
+       urb->sg = NULL;
+
+       spin_lock_irqsave(&xfer->lock, flags);
+       wa = xfer->wa;
+       dev = &wa->usb_iface->dev;
+       if (usb_pipeisoc(xfer->urb->pipe)) {
+               /* Alereon HWA sends all isoc frames in a single transfer. */
+               if (wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC)
+                       xfer->dto_isoc_frame_index += seg->isoc_frame_count;
+               else
+                       xfer->dto_isoc_frame_index += 1;
+               if (xfer->dto_isoc_frame_index < seg->isoc_frame_count) {
+                       data_send_done = 0;
+                       holding_dto = 1; /* checked in error cases. */
+                       /*
+                        * if this is the last isoc frame of the segment, we
+                        * can release DTO after sending this frame.
+                        */
+                       if ((xfer->dto_isoc_frame_index + 1) >=
+                               seg->isoc_frame_count)
+                               release_dto = 1;
+               }
+               dev_dbg(dev, "xfer 0x%08X#%u: isoc frame = %d, holding_dto = %d, release_dto = %d.\n",
+                       wa_xfer_id(xfer), seg->index,
+                       xfer->dto_isoc_frame_index, holding_dto, release_dto);
+       }
+       spin_unlock_irqrestore(&xfer->lock, flags);
+
+       switch (urb->status) {
+       case 0:
+               spin_lock_irqsave(&xfer->lock, flags);
+               seg->result += urb->actual_length;
+               if (data_send_done) {
+                       dev_dbg(dev, "xfer 0x%08X#%u: data out done (%zu bytes)\n",
+                               wa_xfer_id(xfer), seg->index, seg->result);
+                       if (seg->status < WA_SEG_PENDING)
+                               seg->status = WA_SEG_PENDING;
+               } else {
+                       /* should only hit this for isoc xfers. */
+                       /*
+                        * Populate the dto URB with the next isoc frame buffer,
+                        * send the URB and release DTO if we no longer need it.
+                        */
+                        __wa_populate_dto_urb_isoc(xfer, seg,
+                               seg->isoc_frame_offset +
+                               xfer->dto_isoc_frame_index);
+
+                       /* resubmit the URB with the next isoc frame. */
+                       result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC);
+                       if (result < 0) {
+                               dev_err(dev, "xfer 0x%08X#%u: DTO submit failed: %d\n",
+                                      wa_xfer_id(xfer), seg->index, result);
+                               spin_unlock_irqrestore(&xfer->lock, flags);
+                               goto error_dto_submit;
+                       }
+               }
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               if (release_dto) {
+                       __wa_dto_put(wa);
+                       wa_check_for_delayed_rpipes(wa);
+               }
+               break;
+       case -ECONNRESET:       /* URB unlinked; no need to do anything */
+       case -ENOENT:           /* as it was done by the who unlinked us */
+               if (holding_dto) {
+                       __wa_dto_put(wa);
+                       wa_check_for_delayed_rpipes(wa);
+               }
+               break;
+       default:                /* Other errors ... */
+               dev_err(dev, "xfer 0x%08X#%u: data out error %d\n",
+                       wa_xfer_id(xfer), seg->index, urb->status);
+               goto error_default;
+       }
+
+       return;
+
+error_dto_submit:
+error_default:
+       spin_lock_irqsave(&xfer->lock, flags);
+       rpipe = xfer->ep->hcpriv;
+       if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+                   EDC_ERROR_TIMEFRAME)){
+               dev_err(dev, "DTO: URB max acceptable errors exceeded, resetting device\n");
+               wa_reset_all(wa);
+       }
+       if (seg->status != WA_SEG_ERROR) {
+               seg->status = WA_SEG_ERROR;
+               seg->result = urb->status;
+               xfer->segs_done++;
+               __wa_xfer_abort(xfer);
+               rpipe_ready = rpipe_avail_inc(rpipe);
+               done = __wa_xfer_is_done(xfer);
+       }
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       if (holding_dto) {
+               __wa_dto_put(wa);
+               wa_check_for_delayed_rpipes(wa);
+       }
+       if (done)
+               wa_xfer_completion(xfer);
+       if (rpipe_ready)
+               wa_xfer_delayed_run(rpipe);
+
+}
+
+/*
+ * Callback for the isoc packet descriptor phase of the segment request
+ *
+ * Check wa_seg_tr_cb(); most comments also apply here because this
+ * function does almost the same thing and they work closely
+ * together.
+ *
+ * If the seg request has failed but this phase has succeeded,
+ * wa_seg_tr_cb() has already failed the segment and moved the
+ * status to WA_SEG_ERROR, so this will go through 'case 0' and
+ * effectively do nothing.
+ */
+static void wa_seg_iso_pack_desc_cb(struct urb *urb)
 {
        struct wa_seg *seg = urb->context;
        struct wa_xfer *xfer = seg->xfer;
@@ -519,11 +826,10 @@ static void wa_seg_dto_cb(struct urb *urb)
                spin_lock_irqsave(&xfer->lock, flags);
                wa = xfer->wa;
                dev = &wa->usb_iface->dev;
-               dev_dbg(dev, "xfer %p#%u: data out done (%d bytes)\n",
-                       xfer, seg->index, urb->actual_length);
-               if (seg->status < WA_SEG_PENDING)
+               dev_dbg(dev, "iso xfer %08X#%u: packet descriptor done\n",
+                       wa_xfer_id(xfer), seg->index);
+               if (xfer->is_inbound && seg->status < WA_SEG_PENDING)
                        seg->status = WA_SEG_PENDING;
-               seg->result = urb->actual_length;
                spin_unlock_irqrestore(&xfer->lock, flags);
                break;
        case -ECONNRESET:       /* URB unlinked; no need to do anything */
@@ -534,15 +840,15 @@ static void wa_seg_dto_cb(struct urb *urb)
                wa = xfer->wa;
                dev = &wa->usb_iface->dev;
                rpipe = xfer->ep->hcpriv;
-               dev_dbg(dev, "xfer %p#%u: data out error %d\n",
-                       xfer, seg->index, urb->status);
+               pr_err_ratelimited("iso xfer %08X#%u: packet descriptor error %d\n",
+                               wa_xfer_id(xfer), seg->index, urb->status);
                if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
                            EDC_ERROR_TIMEFRAME)){
-                       dev_err(dev, "DTO: URB max acceptable errors "
-                               "exceeded, resetting device\n");
+                       dev_err(dev, "DTO: URB max acceptable errors exceeded, resetting device\n");
                        wa_reset_all(wa);
                }
                if (seg->status != WA_SEG_ERROR) {
+                       usb_unlink_urb(seg->dto_urb);
                        seg->status = WA_SEG_ERROR;
                        seg->result = urb->status;
                        xfer->segs_done++;
@@ -572,11 +878,11 @@ static void wa_seg_dto_cb(struct urb *urb)
  * We have to check before setting the status to WA_SEG_PENDING
  * because sometimes the xfer result callback arrives before this
  * callback (geeeeeeze), so it might happen that we are already in
- * another state. As well, we don't set it if the transfer is inbound,
+ * another state. As well, we don't set it if the transfer is not inbound,
  * as in that case, wa_seg_dto_cb will do it when the OUT data phase
  * finishes.
  */
-static void wa_seg_cb(struct urb *urb)
+static void wa_seg_tr_cb(struct urb *urb)
 {
        struct wa_seg *seg = urb->context;
        struct wa_xfer *xfer = seg->xfer;
@@ -592,8 +898,11 @@ static void wa_seg_cb(struct urb *urb)
                spin_lock_irqsave(&xfer->lock, flags);
                wa = xfer->wa;
                dev = &wa->usb_iface->dev;
-               dev_dbg(dev, "xfer %p#%u: request done\n", xfer, seg->index);
-               if (xfer->is_inbound && seg->status < WA_SEG_PENDING)
+               dev_dbg(dev, "xfer %p ID 0x%08X#%u: request done\n",
+                       xfer, wa_xfer_id(xfer), seg->index);
+               if (xfer->is_inbound &&
+                       seg->status < WA_SEG_PENDING &&
+                       !(usb_pipeisoc(xfer->urb->pipe)))
                        seg->status = WA_SEG_PENDING;
                spin_unlock_irqrestore(&xfer->lock, flags);
                break;
@@ -606,14 +915,16 @@ static void wa_seg_cb(struct urb *urb)
                dev = &wa->usb_iface->dev;
                rpipe = xfer->ep->hcpriv;
                if (printk_ratelimit())
-                       dev_err(dev, "xfer %p#%u: request error %d\n",
-                               xfer, seg->index, urb->status);
+                       dev_err(dev, "xfer %p ID 0x%08X#%u: request error %d\n",
+                               xfer, wa_xfer_id(xfer), seg->index,
+                               urb->status);
                if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
                            EDC_ERROR_TIMEFRAME)){
                        dev_err(dev, "DTO: URB max acceptable errors "
                                "exceeded, resetting device\n");
                        wa_reset_all(wa);
                }
+               usb_unlink_urb(seg->isoc_pack_desc_urb);
                usb_unlink_urb(seg->dto_urb);
                seg->status = WA_SEG_ERROR;
                seg->result = urb->status;
@@ -629,9 +940,11 @@ static void wa_seg_cb(struct urb *urb)
        }
 }
 
-/* allocate an SG list to store bytes_to_transfer bytes and copy the
+/*
+ * Allocate an SG list to store bytes_to_transfer bytes and copy the
  * subset of the in_sg that matches the buffer subset
- * we are about to transfer. */
+ * we are about to transfer.
+ */
 static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg,
        const unsigned int bytes_transferred,
        const unsigned int bytes_to_transfer, unsigned int *out_num_sgs)
@@ -709,6 +1022,75 @@ static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg,
        return out_sg;
 }
 
+/*
+ * Populate DMA buffer info for the isoc dto urb.
+ */
+static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer,
+       struct wa_seg *seg, int curr_iso_frame)
+{
+       seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       seg->dto_urb->sg = NULL;
+       seg->dto_urb->num_sgs = 0;
+       /* dto urb buffer address pulled from iso_frame_desc. */
+       seg->dto_urb->transfer_dma = xfer->urb->transfer_dma +
+               xfer->urb->iso_frame_desc[curr_iso_frame].offset;
+       /* The Alereon HWA sends a single URB with all isoc segs. */
+       if (xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC)
+               seg->dto_urb->transfer_buffer_length = seg->isoc_size;
+       else
+               seg->dto_urb->transfer_buffer_length =
+                       xfer->urb->iso_frame_desc[curr_iso_frame].length;
+}
+
+/*
+ * Populate buffer ptr and size, DMA buffer or SG list for the dto urb.
+ */
+static int __wa_populate_dto_urb(struct wa_xfer *xfer,
+       struct wa_seg *seg, size_t buf_itr_offset, size_t buf_itr_size)
+{
+       int result = 0;
+
+       if (xfer->is_dma) {
+               seg->dto_urb->transfer_dma =
+                       xfer->urb->transfer_dma + buf_itr_offset;
+               seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               seg->dto_urb->sg = NULL;
+               seg->dto_urb->num_sgs = 0;
+       } else {
+               /* do buffer or SG processing. */
+               seg->dto_urb->transfer_flags &=
+                       ~URB_NO_TRANSFER_DMA_MAP;
+               /* this should always be 0 before a resubmit. */
+               seg->dto_urb->num_mapped_sgs = 0;
+
+               if (xfer->urb->transfer_buffer) {
+                       seg->dto_urb->transfer_buffer =
+                               xfer->urb->transfer_buffer +
+                               buf_itr_offset;
+                       seg->dto_urb->sg = NULL;
+                       seg->dto_urb->num_sgs = 0;
+               } else {
+                       seg->dto_urb->transfer_buffer = NULL;
+
+                       /*
+                        * allocate an SG list to store seg_size bytes
+                        * and copy the subset of the xfer->urb->sg that
+                        * matches the buffer subset we are about to
+                        * read.
+                        */
+                       seg->dto_urb->sg = wa_xfer_create_subset_sg(
+                               xfer->urb->sg,
+                               buf_itr_offset, buf_itr_size,
+                               &(seg->dto_urb->num_sgs));
+                       if (!(seg->dto_urb->sg))
+                               result = -ENOMEM;
+               }
+       }
+       seg->dto_urb->transfer_buffer_length = buf_itr_size;
+
+       return result;
+}
+
 /*
  * Allocate the segs array and initialize each of them
  *
@@ -719,13 +1101,14 @@ static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg,
  */
 static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
 {
-       int result, cnt;
+       int result, cnt, iso_frame_offset;
        size_t alloc_size = sizeof(*xfer->seg[0])
                - sizeof(xfer->seg[0]->xfer_hdr) + xfer_hdr_size;
        struct usb_device *usb_dev = xfer->wa->usb_dev;
        const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd;
        struct wa_seg *seg;
        size_t buf_itr, buf_size, buf_itr_size;
+       int xfer_isoc_frame_offset = 0;
 
        result = -ENOMEM;
        xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC);
@@ -733,18 +1116,35 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
                goto error_segs_kzalloc;
        buf_itr = 0;
        buf_size = xfer->urb->transfer_buffer_length;
+       iso_frame_offset = 0;
        for (cnt = 0; cnt < xfer->segs; cnt++) {
-               seg = xfer->seg[cnt] = kmalloc(alloc_size, GFP_ATOMIC);
+               size_t iso_pkt_descr_size = 0;
+               int seg_isoc_frame_count = 0, seg_isoc_size = 0;
+
+               if (usb_pipeisoc(xfer->urb->pipe)) {
+                       seg_isoc_frame_count =
+                               __wa_seg_calculate_isoc_frame_count(xfer,
+                                       xfer_isoc_frame_offset, &seg_isoc_size);
+
+                       iso_pkt_descr_size =
+                               sizeof(struct wa_xfer_packet_info_hwaiso) +
+                               (seg_isoc_frame_count * sizeof(__le16));
+               }
+               seg = xfer->seg[cnt] = kmalloc(alloc_size + iso_pkt_descr_size,
+                                               GFP_ATOMIC);
                if (seg == NULL)
                        goto error_seg_kmalloc;
                wa_seg_init(seg);
                seg->xfer = xfer;
                seg->index = cnt;
-               usb_fill_bulk_urb(&seg->urb, usb_dev,
+               seg->isoc_frame_count = seg_isoc_frame_count;
+               seg->isoc_frame_offset = xfer_isoc_frame_offset;
+               seg->isoc_size = seg_isoc_size;
+               usb_fill_bulk_urb(&seg->tr_urb, usb_dev,
                                  usb_sndbulkpipe(usb_dev,
                                                  dto_epd->bEndpointAddress),
                                  &seg->xfer_hdr, xfer_hdr_size,
-                                 wa_seg_cb, seg);
+                                 wa_seg_tr_cb, seg);
                buf_itr_size = min(buf_size, xfer->seg_size);
                if (xfer->is_inbound == 0 && buf_size > 0) {
                        /* outbound data. */
@@ -756,69 +1156,64 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
                                usb_sndbulkpipe(usb_dev,
                                                dto_epd->bEndpointAddress),
                                NULL, 0, wa_seg_dto_cb, seg);
-                       if (xfer->is_dma) {
-                               seg->dto_urb->transfer_dma =
-                                       xfer->urb->transfer_dma + buf_itr;
-                               seg->dto_urb->transfer_flags |=
-                                       URB_NO_TRANSFER_DMA_MAP;
-                               seg->dto_urb->transfer_buffer = NULL;
-                               seg->dto_urb->sg = NULL;
-                               seg->dto_urb->num_sgs = 0;
+
+                       if (usb_pipeisoc(xfer->urb->pipe)) {
+                               /* iso packet descriptor. */
+                               seg->isoc_pack_desc_urb =
+                                               usb_alloc_urb(0, GFP_ATOMIC);
+                               if (seg->isoc_pack_desc_urb == NULL)
+                                       goto error_iso_pack_desc_alloc;
+                               /*
+                                * The buffer for the isoc packet descriptor
+                                * after the transfer request header in the
+                                * segment object memory buffer.
+                                */
+                               usb_fill_bulk_urb(
+                                       seg->isoc_pack_desc_urb, usb_dev,
+                                       usb_sndbulkpipe(usb_dev,
+                                               dto_epd->bEndpointAddress),
+                                       (void *)(&seg->xfer_hdr) +
+                                               xfer_hdr_size,
+                                       iso_pkt_descr_size,
+                                       wa_seg_iso_pack_desc_cb, seg);
+
+                               /*
+                                * Fill in the xfer buffer information for the
+                                * first isoc frame.  Subsequent frames in this
+                                * segment will be filled in and sent from the
+                                * DTO completion routine, if needed.
+                                */
+                               __wa_populate_dto_urb_isoc(xfer, seg,
+                                       xfer_isoc_frame_offset);
+                               /* adjust starting frame offset for next seg. */
+                               xfer_isoc_frame_offset += seg_isoc_frame_count;
                        } else {
-                               /* do buffer or SG processing. */
-                               seg->dto_urb->transfer_flags &=
-                                       ~URB_NO_TRANSFER_DMA_MAP;
-                               /* this should always be 0 before a resubmit. */
-                               seg->dto_urb->num_mapped_sgs = 0;
-
-                               if (xfer->urb->transfer_buffer) {
-                                       seg->dto_urb->transfer_buffer =
-                                               xfer->urb->transfer_buffer +
-                                               buf_itr;
-                                       seg->dto_urb->sg = NULL;
-                                       seg->dto_urb->num_sgs = 0;
-                               } else {
-                                       /* allocate an SG list to store seg_size
-                                           bytes and copy the subset of the
-                                           xfer->urb->sg that matches the
-                                           buffer subset we are about to read.
-                                       */
-                                       seg->dto_urb->sg =
-                                               wa_xfer_create_subset_sg(
-                                               xfer->urb->sg,
-                                               buf_itr, buf_itr_size,
-                                               &(seg->dto_urb->num_sgs));
-
-                                       if (!(seg->dto_urb->sg)) {
-                                               seg->dto_urb->num_sgs   = 0;
-                                               goto error_sg_alloc;
-                                       }
-
-                                       seg->dto_urb->transfer_buffer = NULL;
-                               }
+                               /* fill in the xfer buffer information. */
+                               result = __wa_populate_dto_urb(xfer, seg,
+                                                       buf_itr, buf_itr_size);
+                               if (result < 0)
+                                       goto error_seg_outbound_populate;
+
+                               buf_itr += buf_itr_size;
+                               buf_size -= buf_itr_size;
                        }
-                       seg->dto_urb->transfer_buffer_length = buf_itr_size;
                }
                seg->status = WA_SEG_READY;
-               buf_itr += buf_itr_size;
-               buf_size -= buf_itr_size;
        }
        return 0;
 
-error_sg_alloc:
+       /*
+        * Free the memory for the current segment which failed to init.
+        * Use the fact that cnt is left at were it failed.  The remaining
+        * segments will be cleaned up by wa_xfer_destroy.
+        */
+error_iso_pack_desc_alloc:
+error_seg_outbound_populate:
        usb_free_urb(xfer->seg[cnt]->dto_urb);
 error_dto_alloc:
        kfree(xfer->seg[cnt]);
-       cnt--;
+       xfer->seg[cnt] = NULL;
 error_seg_kmalloc:
-       /* use the fact that cnt is left at were it failed */
-       for (; cnt >= 0; cnt--) {
-               if (xfer->seg[cnt] && xfer->is_inbound == 0) {
-                       usb_free_urb(xfer->seg[cnt]->dto_urb);
-                       kfree(xfer->seg[cnt]->dto_urb->sg);
-               }
-               kfree(xfer->seg[cnt]);
-       }
 error_segs_kzalloc:
        return result;
 }
@@ -856,21 +1251,45 @@ static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb)
        wa_xfer_id_init(xfer);
        __wa_xfer_setup_hdr0(xfer, xfer_hdr0, xfer_type, xfer_hdr_size);
 
-       /* Fill remainig headers */
+       /* Fill remaining headers */
        xfer_hdr = xfer_hdr0;
-       transfer_size = urb->transfer_buffer_length;
-       xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ?
-               xfer->seg_size : transfer_size;
-       transfer_size -=  xfer->seg_size;
-       for (cnt = 1; cnt < xfer->segs; cnt++) {
-               xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
-               memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
-               xfer_hdr->bTransferSegment = cnt;
-               xfer_hdr->dwTransferLength = transfer_size > xfer->seg_size ?
-                       cpu_to_le32(xfer->seg_size)
-                       : cpu_to_le32(transfer_size);
-               xfer->seg[cnt]->status = WA_SEG_READY;
+       if (xfer_type == WA_XFER_TYPE_ISO) {
+               xfer_hdr0->dwTransferLength =
+                       cpu_to_le32(xfer->seg[0]->isoc_size);
+               for (cnt = 1; cnt < xfer->segs; cnt++) {
+                       struct wa_xfer_packet_info_hwaiso *packet_desc;
+                       struct wa_seg *seg = xfer->seg[cnt];
+
+                       xfer_hdr = &seg->xfer_hdr;
+                       packet_desc = ((void *)xfer_hdr) + xfer_hdr_size;
+                       /*
+                        * Copy values from the 0th header. Segment specific
+                        * values are set below.
+                        */
+                       memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
+                       xfer_hdr->bTransferSegment = cnt;
+                       xfer_hdr->dwTransferLength =
+                               cpu_to_le32(seg->isoc_size);
+                       __wa_setup_isoc_packet_descr(packet_desc, xfer, seg);
+                       seg->status = WA_SEG_READY;
+               }
+       } else {
+               transfer_size = urb->transfer_buffer_length;
+               xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ?
+                       cpu_to_le32(xfer->seg_size) :
+                       cpu_to_le32(transfer_size);
                transfer_size -=  xfer->seg_size;
+               for (cnt = 1; cnt < xfer->segs; cnt++) {
+                       xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
+                       memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
+                       xfer_hdr->bTransferSegment = cnt;
+                       xfer_hdr->dwTransferLength =
+                               transfer_size > xfer->seg_size ?
+                                       cpu_to_le32(xfer->seg_size)
+                                       : cpu_to_le32(transfer_size);
+                       xfer->seg[cnt]->status = WA_SEG_READY;
+                       transfer_size -=  xfer->seg_size;
+               }
        }
        xfer_hdr->bTransferSegment |= 0x80;     /* this is the last segment */
        result = 0;
@@ -885,20 +1304,46 @@ error_setup_sizes:
  * rpipe->seg_lock is held!
  */
 static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
-                          struct wa_seg *seg)
+                          struct wa_seg *seg, int *dto_done)
 {
        int result;
-       result = usb_submit_urb(&seg->urb, GFP_ATOMIC);
+
+       /* default to done unless we encounter a multi-frame isoc segment. */
+       *dto_done = 1;
+
+       /* submit the transfer request. */
+       result = usb_submit_urb(&seg->tr_urb, GFP_ATOMIC);
        if (result < 0) {
-               printk(KERN_ERR "xfer %p#%u: REQ submit failed: %d\n",
-                      xfer, seg->index, result);
+               pr_err("%s: xfer %p#%u: REQ submit failed: %d\n",
+                      __func__, xfer, seg->index, result);
                goto error_seg_submit;
        }
+       /* submit the isoc packet descriptor if present. */
+       if (seg->isoc_pack_desc_urb) {
+               struct wahc *wa = xfer->wa;
+
+               result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC);
+               if (result < 0) {
+                       pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n",
+                              __func__, xfer, seg->index, result);
+                       goto error_iso_pack_desc_submit;
+               }
+               xfer->dto_isoc_frame_index = 0;
+               /*
+                * If this segment contains more than one isoc frame, hold
+                * onto the dto resource until we send all frames.
+                * Only applies to non-Alereon devices.
+                */
+               if (((wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) == 0)
+                       && (seg->isoc_frame_count > 1))
+                       *dto_done = 0;
+       }
+       /* submit the out data if this is an out request. */
        if (seg->dto_urb) {
                result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC);
                if (result < 0) {
-                       printk(KERN_ERR "xfer %p#%u: DTO submit failed: %d\n",
-                              xfer, seg->index, result);
+                       pr_err("%s: xfer %p#%u: DTO submit failed: %d\n",
+                              __func__, xfer, seg->index, result);
                        goto error_dto_submit;
                }
        }
@@ -907,38 +1352,48 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
        return 0;
 
 error_dto_submit:
-       usb_unlink_urb(&seg->urb);
+       usb_unlink_urb(seg->isoc_pack_desc_urb);
+error_iso_pack_desc_submit:
+       usb_unlink_urb(&seg->tr_urb);
 error_seg_submit:
        seg->status = WA_SEG_ERROR;
        seg->result = result;
+       *dto_done = 1;
        return result;
 }
 
 /*
- * Execute more queued request segments until the maximum concurrent allowed
+ * Execute more queued request segments until the maximum concurrent allowed.
+ * Return true if the DTO resource was acquired and released.
  *
  * The ugly unlock/lock sequence on the error path is needed as the
  * xfer->lock normally nests the seg_lock and not viceversa.
- *
  */
-static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
+static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting)
 {
-       int result;
+       int result, dto_acquired = 0, dto_done = 0;
        struct device *dev = &rpipe->wa->usb_iface->dev;
        struct wa_seg *seg;
        struct wa_xfer *xfer;
        unsigned long flags;
 
+       *dto_waiting = 0;
+
        spin_lock_irqsave(&rpipe->seg_lock, flags);
        while (atomic_read(&rpipe->segs_available) > 0
-             && !list_empty(&rpipe->seg_list)) {
+             && !list_empty(&rpipe->seg_list)
+             && (dto_acquired = __wa_dto_try_get(rpipe->wa))) {
                seg = list_first_entry(&(rpipe->seg_list), struct wa_seg,
                                 list_node);
                list_del(&seg->list_node);
                xfer = seg->xfer;
-               result = __wa_seg_submit(rpipe, xfer, seg);
-               dev_dbg(dev, "xfer %p#%u submitted from delayed [%d segments available] %d\n",
-                       xfer, seg->index, atomic_read(&rpipe->segs_available), result);
+               result = __wa_seg_submit(rpipe, xfer, seg, &dto_done);
+               /* release the dto resource if this RPIPE is done with it. */
+               if (dto_done)
+                       __wa_dto_put(rpipe->wa);
+               dev_dbg(dev, "xfer %p ID %08X#%u submitted from delayed [%d segments available] %d\n",
+                       xfer, wa_xfer_id(xfer), seg->index,
+                       atomic_read(&rpipe->segs_available), result);
                if (unlikely(result < 0)) {
                        spin_unlock_irqrestore(&rpipe->seg_lock, flags);
                        spin_lock_irqsave(&xfer->lock, flags);
@@ -948,7 +1403,37 @@ static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
                        spin_lock_irqsave(&rpipe->seg_lock, flags);
                }
        }
+       /*
+        * Mark this RPIPE as waiting if dto was not acquired, there are
+        * delayed segs and no active transfers to wake us up later.
+        */
+       if (!dto_acquired && !list_empty(&rpipe->seg_list)
+               && (atomic_read(&rpipe->segs_available) ==
+                       le16_to_cpu(rpipe->descr.wRequests)))
+               *dto_waiting = 1;
+
        spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+
+       return dto_done;
+}
+
+static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
+{
+       int dto_waiting;
+       int dto_done = __wa_xfer_delayed_run(rpipe, &dto_waiting);
+
+       /*
+        * If this RPIPE is waiting on the DTO resource, add it to the tail of
+        * the waiting list.
+        * Otherwise, if the WA DTO resource was acquired and released by
+        *  __wa_xfer_delayed_run, another RPIPE may have attempted to acquire
+        * DTO and failed during that time.  Check the delayed list and process
+        * any waiters.  Start searching from the next RPIPE index.
+        */
+       if (dto_waiting)
+               wa_add_delayed_rpipe(rpipe->wa, rpipe);
+       else if (dto_done)
+               wa_check_for_delayed_rpipes(rpipe->wa);
 }
 
 /*
@@ -960,7 +1445,7 @@ static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
  */
 static int __wa_xfer_submit(struct wa_xfer *xfer)
 {
-       int result;
+       int result, dto_acquired = 0, dto_done = 0, dto_waiting = 0;
        struct wahc *wa = xfer->wa;
        struct device *dev = &wa->usb_iface->dev;
        unsigned cnt;
@@ -979,27 +1464,58 @@ static int __wa_xfer_submit(struct wa_xfer *xfer)
        result = 0;
        spin_lock_irqsave(&rpipe->seg_lock, flags);
        for (cnt = 0; cnt < xfer->segs; cnt++) {
+               int delay_seg = 1;
+
                available = atomic_read(&rpipe->segs_available);
                empty = list_empty(&rpipe->seg_list);
                seg = xfer->seg[cnt];
-               dev_dbg(dev, "xfer %p#%u: available %u empty %u (%s)\n",
-                       xfer, cnt, available, empty,
-                       available == 0 || !empty ? "delayed" : "submitted");
-               if (available == 0 || !empty) {
-                       dev_dbg(dev, "xfer %p#%u: delayed\n", xfer, cnt);
+               if (available && empty) {
+                       /*
+                        * Only attempt to acquire DTO if we have a segment
+                        * to send.
+                        */
+                       dto_acquired = __wa_dto_try_get(rpipe->wa);
+                       if (dto_acquired) {
+                               delay_seg = 0;
+                               result = __wa_seg_submit(rpipe, xfer, seg,
+                                                       &dto_done);
+                               dev_dbg(dev, "xfer %p ID 0x%08X#%u: available %u empty %u submitted\n",
+                                       xfer, wa_xfer_id(xfer), cnt, available,
+                                       empty);
+                               if (dto_done)
+                                       __wa_dto_put(rpipe->wa);
+
+                               if (result < 0) {
+                                       __wa_xfer_abort(xfer);
+                                       goto error_seg_submit;
+                               }
+                       }
+               }
+
+               if (delay_seg) {
+                       dev_dbg(dev, "xfer %p ID 0x%08X#%u: available %u empty %u delayed\n",
+                               xfer, wa_xfer_id(xfer), cnt, available,  empty);
                        seg->status = WA_SEG_DELAYED;
                        list_add_tail(&seg->list_node, &rpipe->seg_list);
-               } else {
-                       result = __wa_seg_submit(rpipe, xfer, seg);
-                       if (result < 0) {
-                               __wa_xfer_abort(xfer);
-                               goto error_seg_submit;
-                       }
                }
                xfer->segs_submitted++;
        }
 error_seg_submit:
+       /*
+        * Mark this RPIPE as waiting if dto was not acquired, there are
+        * delayed segs and no active transfers to wake us up later.
+        */
+       if (!dto_acquired && !list_empty(&rpipe->seg_list)
+               && (atomic_read(&rpipe->segs_available) ==
+                       le16_to_cpu(rpipe->descr.wRequests)))
+               dto_waiting = 1;
        spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+
+       if (dto_waiting)
+               wa_add_delayed_rpipe(rpipe->wa, rpipe);
+       else if (dto_done)
+               wa_check_for_delayed_rpipes(rpipe->wa);
+
        return result;
 }
 
@@ -1025,7 +1541,7 @@ error_seg_submit:
  * result never kicks in, the xfer will timeout from the USB code and
  * dequeue() will be called.
  */
-static void wa_urb_enqueue_b(struct wa_xfer *xfer)
+static int wa_urb_enqueue_b(struct wa_xfer *xfer)
 {
        int result;
        unsigned long flags;
@@ -1036,18 +1552,22 @@ static void wa_urb_enqueue_b(struct wa_xfer *xfer)
        unsigned done;
 
        result = rpipe_get_by_ep(wa, xfer->ep, urb, xfer->gfp);
-       if (result < 0)
+       if (result < 0) {
+               pr_err("%s: error_rpipe_get\n", __func__);
                goto error_rpipe_get;
+       }
        result = -ENODEV;
        /* FIXME: segmentation broken -- kills DWA */
        mutex_lock(&wusbhc->mutex);             /* get a WUSB dev */
        if (urb->dev == NULL) {
                mutex_unlock(&wusbhc->mutex);
+               pr_err("%s: error usb dev gone\n", __func__);
                goto error_dev_gone;
        }
        wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
        if (wusb_dev == NULL) {
                mutex_unlock(&wusbhc->mutex);
+               pr_err("%s: error wusb dev gone\n", __func__);
                goto error_dev_gone;
        }
        mutex_unlock(&wusbhc->mutex);
@@ -1055,21 +1575,28 @@ static void wa_urb_enqueue_b(struct wa_xfer *xfer)
        spin_lock_irqsave(&xfer->lock, flags);
        xfer->wusb_dev = wusb_dev;
        result = urb->status;
-       if (urb->status != -EINPROGRESS)
+       if (urb->status != -EINPROGRESS) {
+               pr_err("%s: error_dequeued\n", __func__);
                goto error_dequeued;
+       }
 
        result = __wa_xfer_setup(xfer, urb);
-       if (result < 0)
+       if (result < 0) {
+               pr_err("%s: error_xfer_setup\n", __func__);
                goto error_xfer_setup;
+       }
        result = __wa_xfer_submit(xfer);
-       if (result < 0)
+       if (result < 0) {
+               pr_err("%s: error_xfer_submit\n", __func__);
                goto error_xfer_submit;
+       }
        spin_unlock_irqrestore(&xfer->lock, flags);
-       return;
+       return 0;
 
-       /* this is basically wa_xfer_completion() broken up wa_xfer_giveback()
-        * does a wa_xfer_put() that will call wa_xfer_destroy() and clean
-        * upundo setup().
+       /*
+        * this is basically wa_xfer_completion() broken up wa_xfer_giveback()
+        * does a wa_xfer_put() that will call wa_xfer_destroy() and undo
+        * setup().
         */
 error_xfer_setup:
 error_dequeued:
@@ -1081,8 +1608,7 @@ error_dev_gone:
        rpipe_put(xfer->ep->hcpriv);
 error_rpipe_get:
        xfer->result = result;
-       wa_xfer_giveback(xfer);
-       return;
+       return result;
 
 error_xfer_submit:
        done = __wa_xfer_is_done(xfer);
@@ -1090,6 +1616,8 @@ error_xfer_submit:
        spin_unlock_irqrestore(&xfer->lock, flags);
        if (done)
                wa_xfer_completion(xfer);
+       /* return success since the completion routine will run. */
+       return 0;
 }
 
 /*
@@ -1123,7 +1651,8 @@ void wa_urb_enqueue_run(struct work_struct *ws)
                list_del_init(&xfer->list_node);
 
                urb = xfer->urb;
-               wa_urb_enqueue_b(xfer);
+               if (wa_urb_enqueue_b(xfer) < 0)
+                       wa_xfer_giveback(xfer);
                usb_put_urb(urb);       /* taken when queuing */
        }
 }
@@ -1229,7 +1758,19 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep,
                spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags);
                queue_work(wusbd, &wa->xfer_enqueue_work);
        } else {
-               wa_urb_enqueue_b(xfer);
+               result = wa_urb_enqueue_b(xfer);
+               if (result < 0) {
+                       /*
+                        * URB submit/enqueue failed.  Clean up, return an
+                        * error and do not run the callback.  This avoids
+                        * an infinite submit/complete loop.
+                        */
+                       dev_err(dev, "%s: URB enqueue failed: %d\n",
+                          __func__, result);
+                       wa_put(xfer->wa);
+                       wa_xfer_put(xfer);
+                       return result;
+               }
        }
        return 0;
 
@@ -1264,7 +1805,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
        struct wa_xfer *xfer;
        struct wa_seg *seg;
        struct wa_rpipe *rpipe;
-       unsigned cnt;
+       unsigned cnt, done = 0, xfer_abort_pending;
        unsigned rpipe_ready = 0;
 
        xfer = urb->hcpriv;
@@ -1278,6 +1819,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
                goto out;
        }
        spin_lock_irqsave(&xfer->lock, flags);
+       pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer));
        rpipe = xfer->ep->hcpriv;
        if (rpipe == NULL) {
                pr_debug("%s: xfer id 0x%08X has no RPIPE.  %s",
@@ -1293,9 +1835,11 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
        if (xfer->seg == NULL)          /* still hasn't reached */
                goto out_unlock;        /* setup(), enqueue_b() completes */
        /* Ok, the xfer is in flight already, it's been setup and submitted.*/
-       __wa_xfer_abort(xfer);
+       xfer_abort_pending = __wa_xfer_abort(xfer) >= 0;
        for (cnt = 0; cnt < xfer->segs; cnt++) {
                seg = xfer->seg[cnt];
+               pr_debug("%s: xfer id 0x%08X#%d status = %d\n",
+                       __func__, wa_xfer_id(xfer), cnt, seg->status);
                switch (seg->status) {
                case WA_SEG_NOTREADY:
                case WA_SEG_READY:
@@ -1304,42 +1848,50 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
                        WARN_ON(1);
                        break;
                case WA_SEG_DELAYED:
+                       /*
+                        * delete from rpipe delayed list.  If no segments on
+                        * this xfer have been submitted, __wa_xfer_is_done will
+                        * trigger a giveback below.  Otherwise, the submitted
+                        * segments will be completed in the DTI interrupt.
+                        */
                        seg->status = WA_SEG_ABORTED;
                        spin_lock_irqsave(&rpipe->seg_lock, flags2);
                        list_del(&seg->list_node);
                        xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
                        spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
                        break;
-               case WA_SEG_SUBMITTED:
-                       seg->status = WA_SEG_ABORTED;
-                       usb_unlink_urb(&seg->urb);
-                       if (xfer->is_inbound == 0)
-                               usb_unlink_urb(seg->dto_urb);
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
-               case WA_SEG_PENDING:
-                       seg->status = WA_SEG_ABORTED;
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
-               case WA_SEG_DTI_PENDING:
-                       usb_unlink_urb(wa->dti_urb);
-                       seg->status = WA_SEG_ABORTED;
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
                case WA_SEG_DONE:
                case WA_SEG_ERROR:
                case WA_SEG_ABORTED:
                        break;
+                       /*
+                        * In the states below, the HWA device already knows
+                        * about the transfer.  If an abort request was sent,
+                        * allow the HWA to process it and wait for the
+                        * results.  Otherwise, the DTI state and seg completed
+                        * counts can get out of sync.
+                        */
+               case WA_SEG_SUBMITTED:
+               case WA_SEG_PENDING:
+               case WA_SEG_DTI_PENDING:
+                       /*
+                        * Check if the abort was successfully sent.  This could
+                        * be false if the HWA has been removed but we haven't
+                        * gotten the disconnect notification yet.
+                        */
+                       if (!xfer_abort_pending) {
+                               seg->status = WA_SEG_ABORTED;
+                               rpipe_ready = rpipe_avail_inc(rpipe);
+                               xfer->segs_done++;
+                       }
+                       break;
                }
        }
        xfer->result = urb->status;     /* -ENOENT or -ECONNRESET */
-       __wa_xfer_is_done(xfer);
+       done = __wa_xfer_is_done(xfer);
        spin_unlock_irqrestore(&xfer->lock, flags);
-       wa_xfer_completion(xfer);
+       if (done)
+               wa_xfer_completion(xfer);
        if (rpipe_ready)
                wa_xfer_delayed_run(rpipe);
        return 0;
@@ -1409,14 +1961,57 @@ static int wa_xfer_status_to_errno(u8 status)
        return errno;
 }
 
+/*
+ * If a last segment flag and/or a transfer result error is encountered,
+ * no other segment transfer results will be returned from the device.
+ * Mark the remaining submitted or pending xfers as completed so that
+ * the xfer will complete cleanly.
+ */
+static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
+               struct wa_seg *incoming_seg)
+{
+       int index;
+       struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+       for (index = incoming_seg->index + 1; index < xfer->segs_submitted;
+               index++) {
+               struct wa_seg *current_seg = xfer->seg[index];
+
+               BUG_ON(current_seg == NULL);
+
+               switch (current_seg->status) {
+               case WA_SEG_SUBMITTED:
+               case WA_SEG_PENDING:
+               case WA_SEG_DTI_PENDING:
+                       rpipe_avail_inc(rpipe);
+               /*
+                * do not increment RPIPE avail for the WA_SEG_DELAYED case
+                * since it has not been submitted to the RPIPE.
+                */
+               case WA_SEG_DELAYED:
+                       xfer->segs_done++;
+                       current_seg->status = incoming_seg->status;
+                       break;
+               case WA_SEG_ABORTED:
+                       break;
+               default:
+                       WARN(1, "%s: xfer 0x%08X#%d. bad seg status = %d\n",
+                               __func__, wa_xfer_id(xfer), index,
+                               current_seg->status);
+                       break;
+               }
+       }
+}
+
 /*
  * Process a xfer result completion message
  *
- * inbound transfers: need to schedule a DTI read
+ * inbound transfers: need to schedule a buf_in_urb read
  *
  * FIXME: this function needs to be broken up in parts
  */
-static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
+static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer,
+               struct wa_xfer_result *xfer_result)
 {
        int result;
        struct device *dev = &wa->usb_iface->dev;
@@ -1424,8 +2019,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
        u8 seg_idx;
        struct wa_seg *seg;
        struct wa_rpipe *rpipe;
-       struct wa_xfer_result *xfer_result = wa->xfer_result;
-       u8 done = 0;
+       unsigned done = 0;
        u8 usb_status;
        unsigned rpipe_ready = 0;
 
@@ -1436,8 +2030,8 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
        seg = xfer->seg[seg_idx];
        rpipe = xfer->ep->hcpriv;
        usb_status = xfer_result->bTransferStatus;
-       dev_dbg(dev, "xfer %p#%u: bTransferStatus 0x%02x (seg status %u)\n",
-               xfer, seg_idx, usb_status, seg->status);
+       dev_dbg(dev, "xfer %p ID 0x%08X#%u: bTransferStatus 0x%02x (seg status %u)\n",
+               xfer, wa_xfer_id(xfer), seg_idx, usb_status, seg->status);
        if (seg->status == WA_SEG_ABORTED
            || seg->status == WA_SEG_ERROR)     /* already handled */
                goto segment_aborted;
@@ -1453,12 +2047,19 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
                seg->result = wa_xfer_status_to_errno(usb_status);
                dev_err(dev, "DTI: xfer %p#:%08X:%u failed (0x%02x)\n",
                        xfer, xfer->id, seg->index, usb_status);
+               seg->status = ((usb_status & 0x7F) == WA_XFER_STATUS_ABORTED) ?
+                       WA_SEG_ABORTED : WA_SEG_ERROR;
                goto error_complete;
        }
        /* FIXME: we ignore warnings, tally them for stats */
        if (usb_status & 0x40)          /* Warning?... */
                usb_status = 0;         /* ... pass */
-       if (xfer->is_inbound) { /* IN data phase: read to buffer */
+       if (usb_pipeisoc(xfer->urb->pipe)) {
+               /* set up WA state to read the isoc packet status next. */
+               wa->dti_isoc_xfer_in_progress = wa_xfer_id(xfer);
+               wa->dti_isoc_xfer_seg = seg_idx;
+               wa->dti_state = WA_DTI_ISOC_PACKET_STATUS_PENDING;
+       } else if (xfer->is_inbound) {  /* IN data phase: read to buffer */
                seg->status = WA_SEG_DTI_PENDING;
                BUG_ON(wa->buf_in_urb->status == -EINPROGRESS);
                /* this should always be 0 before a resubmit. */
@@ -1535,12 +2136,14 @@ error_submit_buf_in:
                        xfer, seg_idx, result);
        seg->result = result;
        kfree(wa->buf_in_urb->sg);
+       wa->buf_in_urb->sg = NULL;
 error_sg_alloc:
        __wa_xfer_abort(xfer);
-error_complete:
        seg->status = WA_SEG_ERROR;
+error_complete:
        xfer->segs_done++;
        rpipe_ready = rpipe_avail_inc(rpipe);
+       wa_complete_remaining_xfer_segs(xfer, seg);
        done = __wa_xfer_is_done(xfer);
        /*
         * queue work item to clear STALL for control endpoints.
@@ -1552,10 +2155,8 @@ error_complete:
 
                dev_info(dev, "Control EP stall.  Queue delayed work.\n");
                spin_lock_irq(&wa->xfer_list_lock);
-               /* remove xfer from xfer_list. */
-               list_del(&xfer->list_node);
-               /* add xfer to xfer_errored_list. */
-               list_add_tail(&xfer->list_node, &wa->xfer_errored_list);
+               /* move xfer from xfer_list to xfer_errored_list. */
+               list_move_tail(&xfer->list_node, &wa->xfer_errored_list);
                spin_unlock_irq(&wa->xfer_list_lock);
                spin_unlock_irqrestore(&xfer->lock, flags);
                queue_work(wusbd, &wa->xfer_error_work);
@@ -1586,6 +2187,90 @@ segment_aborted:
        spin_unlock_irqrestore(&xfer->lock, flags);
 }
 
+/*
+ * Process a isochronous packet status message
+ *
+ * inbound transfers: need to schedule a buf_in_urb read
+ */
+static void wa_process_iso_packet_status(struct wahc *wa, struct urb *urb)
+{
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_xfer_packet_status_hwaiso *packet_status;
+       struct wa_xfer_packet_status_len_hwaiso *status_array;
+       struct wa_xfer *xfer;
+       unsigned long flags;
+       struct wa_seg *seg;
+       struct wa_rpipe *rpipe;
+       unsigned done = 0;
+       unsigned rpipe_ready = 0, seg_index;
+       int expected_size;
+
+       /* We have a xfer result buffer; check it */
+       dev_dbg(dev, "DTI: isoc packet status %d bytes at %p\n",
+               urb->actual_length, urb->transfer_buffer);
+       packet_status = (struct wa_xfer_packet_status_hwaiso *)(wa->dti_buf);
+       if (packet_status->bPacketType != WA_XFER_ISO_PACKET_STATUS) {
+               dev_err(dev, "DTI Error: isoc packet status--bad type 0x%02x\n",
+                       packet_status->bPacketType);
+               goto error_parse_buffer;
+       }
+       xfer = wa_xfer_get_by_id(wa, wa->dti_isoc_xfer_in_progress);
+       if (xfer == NULL) {
+               dev_err(dev, "DTI Error: isoc packet status--unknown xfer 0x%08x\n",
+                       wa->dti_isoc_xfer_in_progress);
+               goto error_parse_buffer;
+       }
+       spin_lock_irqsave(&xfer->lock, flags);
+       if (unlikely(wa->dti_isoc_xfer_seg >= xfer->segs))
+               goto error_bad_seg;
+       seg = xfer->seg[wa->dti_isoc_xfer_seg];
+       rpipe = xfer->ep->hcpriv;
+       expected_size = sizeof(*packet_status) +
+                       (sizeof(packet_status->PacketStatus[0]) *
+                       seg->isoc_frame_count);
+       if (urb->actual_length != expected_size) {
+               dev_err(dev, "DTI Error: isoc packet status--bad urb length (%d bytes vs %d needed)\n",
+                       urb->actual_length, expected_size);
+               goto error_bad_seg;
+       }
+       if (le16_to_cpu(packet_status->wLength) != expected_size) {
+               dev_err(dev, "DTI Error: isoc packet status--bad length %u\n",
+                       le16_to_cpu(packet_status->wLength));
+               goto error_bad_seg;
+       }
+       /* isoc packet status and lengths back xfer urb. */
+       status_array = packet_status->PacketStatus;
+       for (seg_index = 0; seg_index < seg->isoc_frame_count; ++seg_index) {
+               xfer->urb->iso_frame_desc[seg->index].status =
+                       wa_xfer_status_to_errno(
+                       le16_to_cpu(status_array[seg_index].PacketStatus));
+               xfer->urb->iso_frame_desc[seg->index].actual_length =
+                       le16_to_cpu(status_array[seg_index].PacketLength);
+       }
+
+       if (!xfer->is_inbound) {
+               /* OUT transfer, complete it -- */
+               seg->status = WA_SEG_DONE;
+               xfer->segs_done++;
+               rpipe_ready = rpipe_avail_inc(rpipe);
+               done = __wa_xfer_is_done(xfer);
+       }
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
+       if (done)
+               wa_xfer_completion(xfer);
+       if (rpipe_ready)
+               wa_xfer_delayed_run(rpipe);
+       wa_xfer_put(xfer);
+       return;
+
+error_bad_seg:
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       wa_xfer_put(xfer);
+error_parse_buffer:
+       return;
+}
+
 /*
  * Callback for the IN data phase
  *
@@ -1687,56 +2372,61 @@ static void wa_buf_in_cb(struct urb *urb)
  * We go back to OFF when we detect a ENOENT or ESHUTDOWN (or too many
  * errors) in the URBs.
  */
-static void wa_xfer_result_cb(struct urb *urb)
+static void wa_dti_cb(struct urb *urb)
 {
        int result;
        struct wahc *wa = urb->context;
        struct device *dev = &wa->usb_iface->dev;
-       struct wa_xfer_result *xfer_result;
        u32 xfer_id;
-       struct wa_xfer *xfer;
        u8 usb_status;
 
        BUG_ON(wa->dti_urb != urb);
        switch (wa->dti_urb->status) {
        case 0:
-               /* We have a xfer result buffer; check it */
-               dev_dbg(dev, "DTI: xfer result %d bytes at %p\n",
-                       urb->actual_length, urb->transfer_buffer);
-               if (wa->dti_urb->actual_length != sizeof(*xfer_result)) {
-                       dev_err(dev, "DTI Error: xfer result--bad size "
-                               "xfer result (%d bytes vs %zu needed)\n",
-                               urb->actual_length, sizeof(*xfer_result));
-                       break;
-               }
-               xfer_result = wa->xfer_result;
-               if (xfer_result->hdr.bLength != sizeof(*xfer_result)) {
-                       dev_err(dev, "DTI Error: xfer result--"
-                               "bad header length %u\n",
-                               xfer_result->hdr.bLength);
-                       break;
-               }
-               if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) {
-                       dev_err(dev, "DTI Error: xfer result--"
-                               "bad header type 0x%02x\n",
-                               xfer_result->hdr.bNotifyType);
-                       break;
-               }
-               usb_status = xfer_result->bTransferStatus & 0x3f;
-               if (usb_status == WA_XFER_STATUS_NOT_FOUND)
-                       /* taken care of already */
-                       break;
-               xfer_id = xfer_result->dwTransferID;
-               xfer = wa_xfer_get_by_id(wa, xfer_id);
-               if (xfer == NULL) {
-                       /* FIXME: transaction might have been cancelled */
-                       dev_err(dev, "DTI Error: xfer result--"
-                               "unknown xfer 0x%08x (status 0x%02x)\n",
-                               xfer_id, usb_status);
-                       break;
+               if (wa->dti_state == WA_DTI_TRANSFER_RESULT_PENDING) {
+                       struct wa_xfer_result *xfer_result;
+                       struct wa_xfer *xfer;
+
+                       /* We have a xfer result buffer; check it */
+                       dev_dbg(dev, "DTI: xfer result %d bytes at %p\n",
+                               urb->actual_length, urb->transfer_buffer);
+                       if (urb->actual_length != sizeof(*xfer_result)) {
+                               dev_err(dev, "DTI Error: xfer result--bad size xfer result (%d bytes vs %zu needed)\n",
+                                       urb->actual_length,
+                                       sizeof(*xfer_result));
+                               break;
+                       }
+                       xfer_result = (struct wa_xfer_result *)(wa->dti_buf);
+                       if (xfer_result->hdr.bLength != sizeof(*xfer_result)) {
+                               dev_err(dev, "DTI Error: xfer result--bad header length %u\n",
+                                       xfer_result->hdr.bLength);
+                               break;
+                       }
+                       if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) {
+                               dev_err(dev, "DTI Error: xfer result--bad header type 0x%02x\n",
+                                       xfer_result->hdr.bNotifyType);
+                               break;
+                       }
+                       usb_status = xfer_result->bTransferStatus & 0x3f;
+                       if (usb_status == WA_XFER_STATUS_NOT_FOUND)
+                               /* taken care of already */
+                               break;
+                       xfer_id = le32_to_cpu(xfer_result->dwTransferID);
+                       xfer = wa_xfer_get_by_id(wa, xfer_id);
+                       if (xfer == NULL) {
+                               /* FIXME: transaction not found. */
+                               dev_err(dev, "DTI Error: xfer result--unknown xfer 0x%08x (status 0x%02x)\n",
+                                       xfer_id, usb_status);
+                               break;
+                       }
+                       wa_xfer_result_chew(wa, xfer, xfer_result);
+                       wa_xfer_put(xfer);
+               } else if (wa->dti_state == WA_DTI_ISOC_PACKET_STATUS_PENDING) {
+                       wa_process_iso_packet_status(wa, urb);
+               } else {
+                       dev_err(dev, "DTI Error: unexpected EP state = %d\n",
+                               wa->dti_state);
                }
-               wa_xfer_result_chew(wa, xfer);
-               wa_xfer_put(xfer);
                break;
        case -ENOENT:           /* (we killed the URB)...so, no broadcast */
        case -ESHUTDOWN:        /* going away! */
@@ -1777,7 +2467,7 @@ out:
  * don't really set it up and start it until the first xfer complete
  * notification arrives, which is what we do here.
  *
- * Follow up in wa_xfer_result_cb(), as that's where the whole state
+ * Follow up in wa_dti_cb(), as that's where the whole state
  * machine starts.
  *
  * So here we just initialize the DTI URB for reading transfer result
@@ -1813,8 +2503,8 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
        usb_fill_bulk_urb(
                wa->dti_urb, wa->usb_dev,
                usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
-               wa->xfer_result, wa->xfer_result_size,
-               wa_xfer_result_cb, wa);
+               wa->dti_buf, wa->dti_buf_size,
+               wa_dti_cb, wa);
 
        wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (wa->buf_in_urb == NULL) {
@@ -1836,6 +2526,7 @@ out:
 
 error_dti_urb_submit:
        usb_put_urb(wa->buf_in_urb);
+       wa->buf_in_urb = NULL;
 error_buf_in_urb_alloc:
        usb_put_urb(wa->dti_urb);
        wa->dti_urb = NULL;
index 9209eafc75b1f004e407965c32c3a683abdf5ed2..80079b8fed155c39f109bd65d12e4e5e155a1b3c 100644 (file)
@@ -244,7 +244,7 @@ static ssize_t uwb_dev_RSSI_store(struct device *dev,
 static DEVICE_ATTR(RSSI, S_IRUGO | S_IWUSR, uwb_dev_RSSI_show, uwb_dev_RSSI_store);
 
 
-static struct attribute *dev_attrs[] = {
+static struct attribute *uwb_dev_attrs[] = {
        &dev_attr_EUI_48.attr,
        &dev_attr_DevAddr.attr,
        &dev_attr_BPST.attr,
@@ -253,20 +253,10 @@ static struct attribute *dev_attrs[] = {
        &dev_attr_RSSI.attr,
        NULL,
 };
-
-static struct attribute_group dev_attr_group = {
-       .attrs = dev_attrs,
-};
-
-static const struct attribute_group *groups[] = {
-       &dev_attr_group,
-       NULL,
-};
+ATTRIBUTE_GROUPS(uwb_dev);
 
 /**
  * Device SYSFS registration
- *
- *
  */
 static int __uwb_dev_sys_add(struct uwb_dev *uwb_dev, struct device *parent_dev)
 {
@@ -276,7 +266,7 @@ static int __uwb_dev_sys_add(struct uwb_dev *uwb_dev, struct device *parent_dev)
        /* Device sysfs files are only useful for neighbor devices not
           local radio controllers. */
        if (&uwb_dev->rc->uwb_dev != uwb_dev)
-               dev->groups = groups;
+               dev->groups = uwb_dev_groups;
        dev->parent = parent_dev;
        dev_set_drvdata(dev, uwb_dev);
 
index 5c5b3fc9088a30d25fd526fca9427006e9f9d23e..e3ed6ff6a48124b5cbdd5b5c4f11dbe07659fcec 100644 (file)
@@ -201,6 +201,7 @@ static ssize_t capability_id_show(struct device *dev, struct device_attribute *a
 
        return sprintf(buf, "0x%02x\n", umc->cap_id);
 }
+static DEVICE_ATTR_RO(capability_id);
 
 static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
@@ -208,12 +209,14 @@ static ssize_t version_show(struct device *dev, struct device_attribute *attr, c
 
        return sprintf(buf, "0x%04x\n", umc->version);
 }
+static DEVICE_ATTR_RO(version);
 
-static struct device_attribute umc_dev_attrs[] = {
-       __ATTR_RO(capability_id),
-       __ATTR_RO(version),
-       __ATTR_NULL,
+static struct attribute *umc_dev_attrs[] = {
+       &dev_attr_capability_id.attr,
+       &dev_attr_version.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(umc_dev);
 
 struct bus_type umc_bus_type = {
        .name           = "umc",
@@ -222,7 +225,7 @@ struct bus_type umc_bus_type = {
        .remove         = umc_device_remove,
        .suspend        = umc_device_suspend,
        .resume         = umc_device_resume,
-       .dev_attrs      = umc_dev_attrs,
+       .dev_groups     = umc_dev_groups,
 };
 EXPORT_SYMBOL_GPL(umc_bus_type);
 
index ce5221fa393a8db2ca9f4de61817ae230f3f0bc8..e663921eebb6f899db67e352077689b6e84d270f 100644 (file)
@@ -1056,7 +1056,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                if (data_direction != DMA_NONE) {
                        ret = vhost_scsi_map_iov_to_sgl(cmd,
                                        &vq->iov[data_first], data_num,
-                                       data_direction == DMA_TO_DEVICE);
+                                       data_direction == DMA_FROM_DEVICE);
                        if (unlikely(ret)) {
                                vq_err(vq, "Failed to map iov to sgl\n");
                                goto err_free;
index a54ccdc4d6618bf4bdcfd5202f3e8489f22cec34..22ad85242e5b923d84eee490fc56e8ed0f29c3b7 100644 (file)
@@ -361,37 +361,13 @@ void au1100fb_fb_rotate(struct fb_info *fbi, int angle)
 int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
 {
        struct au1100fb_device *fbdev;
-       unsigned int len;
-       unsigned long start=0, off;
 
        fbdev = to_au1100fb_device(fbi);
 
-       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
-               return -EINVAL;
-       }
-
-       start = fbdev->fb_phys & PAGE_MASK;
-       len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
-
-       off = vma->vm_pgoff << PAGE_SHIFT;
-
-       if ((vma->vm_end - vma->vm_start + off) > len) {
-               return -EINVAL;
-       }
-
-       off += start;
-       vma->vm_pgoff = off >> PAGE_SHIFT;
-
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
        pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6
 
-       if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-                               vma->vm_end - vma->vm_start,
-                               vma->vm_page_prot)) {
-               return -EAGAIN;
-       }
-
-       return 0;
+       return vm_iomap_memory(vma, fbdev->fb_phys, fbdev->fb_len);
 }
 
 static struct fb_ops au1100fb_ops =
index 301224ecc9507186e78e9cc14576e7a5316b227d..1d02897d17f27e91ace5622b309c730a8364187c 100644 (file)
@@ -1233,34 +1233,13 @@ static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi)
  * method mainly to allow the use of the TLB streaming flag (CCA=6)
  */
 static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
-
 {
-       unsigned int len;
-       unsigned long start=0, off;
        struct au1200fb_device *fbdev = info->par;
 
-       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
-               return -EINVAL;
-       }
-
-       start = fbdev->fb_phys & PAGE_MASK;
-       len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
-
-       off = vma->vm_pgoff << PAGE_SHIFT;
-
-       if ((vma->vm_end - vma->vm_start + off) > len) {
-               return -EINVAL;
-       }
-
-       off += start;
-       vma->vm_pgoff = off >> PAGE_SHIFT;
-
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
        pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */
 
-       return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-                                 vma->vm_end - vma->vm_start,
-                                 vma->vm_page_prot);
+       return vm_iomap_memory(vma, fbdev->fb_phys, fbdev->fb_len);
 }
 
 static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata)
index 0393d827dd44bcfa6f472d78c11823892b944af3..f7447f7004fb12babc4141d55ee70698806f8a46 100644 (file)
@@ -118,7 +118,7 @@ static const struct backlight_ops atmel_pwm_bl_ops = {
        .update_status  = atmel_pwm_bl_set_intensity,
 };
 
-static int __init atmel_pwm_bl_probe(struct platform_device *pdev)
+static int atmel_pwm_bl_probe(struct platform_device *pdev)
 {
        struct backlight_properties props;
        const struct atmel_pwm_bl_platform_data *pdata;
@@ -202,7 +202,7 @@ err_free_mem:
        return retval;
 }
 
-static int __exit atmel_pwm_bl_remove(struct platform_device *pdev)
+static int atmel_pwm_bl_remove(struct platform_device *pdev)
 {
        struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);
 
@@ -220,10 +220,11 @@ static struct platform_driver atmel_pwm_bl_driver = {
                .name = "atmel-pwm-bl",
        },
        /* REVISIT add suspend() and resume() */
-       .remove = __exit_p(atmel_pwm_bl_remove),
+       .probe = atmel_pwm_bl_probe,
+       .remove = atmel_pwm_bl_remove,
 };
 
-module_platform_driver_probe(atmel_pwm_bl_driver, atmel_pwm_bl_probe);
+module_platform_driver(atmel_pwm_bl_driver);
 
 MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>");
 MODULE_DESCRIPTION("Atmel PWM backlight driver");
index 1b035b2eb6b611a075e633b3cf020e2dbfcb5700..1129d0e9e6403dbb6c68a0478fda17bc2117b7b9 100644 (file)
@@ -16,6 +16,7 @@ if EXYNOS_VIDEO
 config EXYNOS_MIPI_DSI
        bool "EXYNOS MIPI DSI driver support."
        depends on ARCH_S5PV210 || ARCH_EXYNOS
+       select GENERIC_PHY
        help
          This enables support for MIPI-DSI device.
 
@@ -29,7 +30,7 @@ config EXYNOS_LCD_S6E8AX0
 
 config EXYNOS_DP
        bool "EXYNOS DP driver support"
-       depends on ARCH_EXYNOS
+       depends on OF && ARCH_EXYNOS
        default n
        help
          This enables support for DP device.
index 12bbede3b091e68ae0600b7fb55c765052d2f66d..5e1a7158005196572459dbf7bfbf561495210410 100644 (file)
@@ -19,8 +19,7 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/of.h>
-
-#include <video/exynos_dp.h>
+#include <linux/phy/phy.h>
 
 #include "exynos_dp_core.h"
 
@@ -894,26 +893,17 @@ static void exynos_dp_hotplug(struct work_struct *work)
                dev_err(dp->dev, "unable to config video\n");
 }
 
-#ifdef CONFIG_OF
-static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
+static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
 {
        struct device_node *dp_node = dev->of_node;
-       struct exynos_dp_platdata *pd;
        struct video_info *dp_video_config;
 
-       pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
-       if (!pd) {
-               dev_err(dev, "memory allocation for pdata failed\n");
-               return ERR_PTR(-ENOMEM);
-       }
        dp_video_config = devm_kzalloc(dev,
                                sizeof(*dp_video_config), GFP_KERNEL);
-
        if (!dp_video_config) {
                dev_err(dev, "memory allocation for video config failed\n");
                return ERR_PTR(-ENOMEM);
        }
-       pd->video_info = dp_video_config;
 
        dp_video_config->h_sync_polarity =
                of_property_read_bool(dp_node, "hsync-active-high");
@@ -960,7 +950,7 @@ static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
                return ERR_PTR(-EINVAL);
        }
 
-       return pd;
+       return dp_video_config;
 }
 
 static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
@@ -971,8 +961,11 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
 
        dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
        if (!dp_phy_node) {
-               dev_err(dp->dev, "could not find dptx-phy node\n");
-               return -ENODEV;
+               dp->phy = devm_phy_get(dp->dev, "dp");
+               if (IS_ERR(dp->phy))
+                       return PTR_ERR(dp->phy);
+               else
+                       return 0;
        }
 
        if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
@@ -1003,48 +996,34 @@ err:
 
 static void exynos_dp_phy_init(struct exynos_dp_device *dp)
 {
-       u32 reg;
-
-       reg = __raw_readl(dp->phy_addr);
-       reg |= dp->enable_mask;
-       __raw_writel(reg, dp->phy_addr);
-}
-
-static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
-{
-       u32 reg;
-
-       reg = __raw_readl(dp->phy_addr);
-       reg &= ~(dp->enable_mask);
-       __raw_writel(reg, dp->phy_addr);
-}
-#else
-static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
-{
-       return NULL;
-}
-
-static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
-{
-       return -EINVAL;
-}
-
-static void exynos_dp_phy_init(struct exynos_dp_device *dp)
-{
-       return;
+       if (dp->phy) {
+               phy_power_on(dp->phy);
+       } else if (dp->phy_addr) {
+               u32 reg;
+
+               reg = __raw_readl(dp->phy_addr);
+               reg |= dp->enable_mask;
+               __raw_writel(reg, dp->phy_addr);
+       }
 }
 
 static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
 {
-       return;
+       if (dp->phy) {
+               phy_power_off(dp->phy);
+       } else if (dp->phy_addr) {
+               u32 reg;
+
+               reg = __raw_readl(dp->phy_addr);
+               reg &= ~(dp->enable_mask);
+               __raw_writel(reg, dp->phy_addr);
+       }
 }
-#endif /* CONFIG_OF */
 
 static int exynos_dp_probe(struct platform_device *pdev)
 {
        struct resource *res;
        struct exynos_dp_device *dp;
-       struct exynos_dp_platdata *pdata;
 
        int ret = 0;
 
@@ -1057,21 +1036,13 @@ static int exynos_dp_probe(struct platform_device *pdev)
 
        dp->dev = &pdev->dev;
 
-       if (pdev->dev.of_node) {
-               pdata = exynos_dp_dt_parse_pdata(&pdev->dev);
-               if (IS_ERR(pdata))
-                       return PTR_ERR(pdata);
+       dp->video_info = exynos_dp_dt_parse_pdata(&pdev->dev);
+       if (IS_ERR(dp->video_info))
+               return PTR_ERR(dp->video_info);
 
-               ret = exynos_dp_dt_parse_phydata(dp);
-               if (ret)
-                       return ret;
-       } else {
-               pdata = pdev->dev.platform_data;
-               if (!pdata) {
-                       dev_err(&pdev->dev, "no platform data\n");
-                       return -EINVAL;
-               }
-       }
+       ret = exynos_dp_dt_parse_phydata(dp);
+       if (ret)
+               return ret;
 
        dp->clock = devm_clk_get(&pdev->dev, "dp");
        if (IS_ERR(dp->clock)) {
@@ -1095,15 +1066,7 @@ static int exynos_dp_probe(struct platform_device *pdev)
 
        INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
 
-       dp->video_info = pdata->video_info;
-
-       if (pdev->dev.of_node) {
-               if (dp->phy_addr)
-                       exynos_dp_phy_init(dp);
-       } else {
-               if (pdata->phy_init)
-                       pdata->phy_init();
-       }
+       exynos_dp_phy_init(dp);
 
        exynos_dp_init_dp(dp);
 
@@ -1121,18 +1084,11 @@ static int exynos_dp_probe(struct platform_device *pdev)
 
 static int exynos_dp_remove(struct platform_device *pdev)
 {
-       struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
        struct exynos_dp_device *dp = platform_get_drvdata(pdev);
 
        flush_work(&dp->hotplug_work);
 
-       if (pdev->dev.of_node) {
-               if (dp->phy_addr)
-                       exynos_dp_phy_exit(dp);
-       } else {
-               if (pdata->phy_exit)
-                       pdata->phy_exit();
-       }
+       exynos_dp_phy_exit(dp);
 
        clk_disable_unprepare(dp->clock);
 
@@ -1143,20 +1099,13 @@ static int exynos_dp_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM_SLEEP
 static int exynos_dp_suspend(struct device *dev)
 {
-       struct exynos_dp_platdata *pdata = dev->platform_data;
        struct exynos_dp_device *dp = dev_get_drvdata(dev);
 
        disable_irq(dp->irq);
 
        flush_work(&dp->hotplug_work);
 
-       if (dev->of_node) {
-               if (dp->phy_addr)
-                       exynos_dp_phy_exit(dp);
-       } else {
-               if (pdata->phy_exit)
-                       pdata->phy_exit();
-       }
+       exynos_dp_phy_exit(dp);
 
        clk_disable_unprepare(dp->clock);
 
@@ -1165,16 +1114,9 @@ static int exynos_dp_suspend(struct device *dev)
 
 static int exynos_dp_resume(struct device *dev)
 {
-       struct exynos_dp_platdata *pdata = dev->platform_data;
        struct exynos_dp_device *dp = dev_get_drvdata(dev);
 
-       if (dev->of_node) {
-               if (dp->phy_addr)
-                       exynos_dp_phy_init(dp);
-       } else {
-               if (pdata->phy_init)
-                       pdata->phy_init();
-       }
+       exynos_dp_phy_init(dp);
 
        clk_prepare_enable(dp->clock);
 
@@ -1203,7 +1145,7 @@ static struct platform_driver exynos_dp_driver = {
                .name   = "exynos-dp",
                .owner  = THIS_MODULE,
                .pm     = &exynos_dp_pm_ops,
-               .of_match_table = of_match_ptr(exynos_dp_match),
+               .of_match_table = exynos_dp_match,
        },
 };
 
index 6c567bbf2fb8fcec22f4f7949916e5a515b7b573..607e36d0c147ada6fc2203a7dc657d2490c1a91d 100644 (file)
 #ifndef _EXYNOS_DP_CORE_H
 #define _EXYNOS_DP_CORE_H
 
+#define DP_TIMEOUT_LOOP_COUNT 100
+#define MAX_CR_LOOP 5
+#define MAX_EQ_LOOP 5
+
+enum link_rate_type {
+       LINK_RATE_1_62GBPS = 0x06,
+       LINK_RATE_2_70GBPS = 0x0a
+};
+
+enum link_lane_count_type {
+       LANE_COUNT1 = 1,
+       LANE_COUNT2 = 2,
+       LANE_COUNT4 = 4
+};
+
+enum link_training_state {
+       START,
+       CLOCK_RECOVERY,
+       EQUALIZER_TRAINING,
+       FINISHED,
+       FAILED
+};
+
+enum voltage_swing_level {
+       VOLTAGE_LEVEL_0,
+       VOLTAGE_LEVEL_1,
+       VOLTAGE_LEVEL_2,
+       VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+       PRE_EMPHASIS_LEVEL_0,
+       PRE_EMPHASIS_LEVEL_1,
+       PRE_EMPHASIS_LEVEL_2,
+       PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+       PRBS7,
+       D10_2,
+       TRAINING_PTN1,
+       TRAINING_PTN2,
+       DP_NONE
+};
+
+enum color_space {
+       COLOR_RGB,
+       COLOR_YCBCR422,
+       COLOR_YCBCR444
+};
+
+enum color_depth {
+       COLOR_6,
+       COLOR_8,
+       COLOR_10,
+       COLOR_12
+};
+
+enum color_coefficient {
+       COLOR_YCBCR601,
+       COLOR_YCBCR709
+};
+
+enum dynamic_range {
+       VESA,
+       CEA
+};
+
+enum pll_status {
+       PLL_UNLOCKED,
+       PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+       CALCULATED_M,
+       REGISTER_M
+};
+
+enum video_timing_recognition_type {
+       VIDEO_TIMING_FROM_CAPTURE,
+       VIDEO_TIMING_FROM_REGISTER
+};
+
+enum analog_power_block {
+       AUX_BLOCK,
+       CH0_BLOCK,
+       CH1_BLOCK,
+       CH2_BLOCK,
+       CH3_BLOCK,
+       ANALOG_TOTAL,
+       POWER_ALL
+};
+
 enum dp_irq_type {
        DP_IRQ_TYPE_HP_CABLE_IN,
        DP_IRQ_TYPE_HP_CABLE_OUT,
@@ -20,6 +113,22 @@ enum dp_irq_type {
        DP_IRQ_TYPE_UNKNOWN,
 };
 
+struct video_info {
+       char *name;
+
+       bool h_sync_polarity;
+       bool v_sync_polarity;
+       bool interlaced;
+
+       enum color_space color_space;
+       enum dynamic_range dynamic_range;
+       enum color_coefficient ycbcr_coeff;
+       enum color_depth color_depth;
+
+       enum link_rate_type link_rate;
+       enum link_lane_count_type lane_count;
+};
+
 struct link_train {
        int eq_loop;
        int cr_loop[4];
@@ -42,6 +151,7 @@ struct exynos_dp_device {
        struct video_info       *video_info;
        struct link_train       link_train;
        struct work_struct      hotplug_work;
+       struct phy              *phy;
 };
 
 /* exynos_dp_reg.c */
index 29d9d035c73a76e7313650fcbb6045b7dd0cfa38..b70da5052ff0287233b3668574df40ed5b7e8fb1 100644 (file)
@@ -14,8 +14,6 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 
-#include <video/exynos_dp.h>
-
 #include "exynos_dp_core.h"
 #include "exynos_dp_reg.h"
 
index 32e540600f995b42937653832c3e4d8508012c50..00b3a52c1d68766d42cab3c65a1a9405d4a04617 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/interrupt.h>
 #include <linux/kthread.h>
 #include <linux/notifier.h>
+#include <linux/phy/phy.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/err.h>
@@ -156,8 +157,7 @@ static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
                exynos_mipi_regulator_enable(dsim);
 
                /* enable MIPI-DSI PHY. */
-               if (dsim->pd->phy_enable)
-                       dsim->pd->phy_enable(pdev, true);
+               phy_power_on(dsim->phy);
 
                clk_enable(dsim->clock);
 
@@ -373,6 +373,10 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
                return ret;
        }
 
+       dsim->phy = devm_phy_get(&pdev->dev, "dsim");
+       if (IS_ERR(dsim->phy))
+               return PTR_ERR(dsim->phy);
+
        dsim->clock = devm_clk_get(&pdev->dev, "dsim0");
        if (IS_ERR(dsim->clock)) {
                dev_err(&pdev->dev, "failed to get dsim clock source\n");
@@ -439,8 +443,7 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
        exynos_mipi_regulator_enable(dsim);
 
        /* enable MIPI-DSI PHY. */
-       if (dsim->pd->phy_enable)
-               dsim->pd->phy_enable(pdev, true);
+       phy_power_on(dsim->phy);
 
        exynos_mipi_update_cfg(dsim);
 
@@ -504,9 +507,8 @@ static int exynos_mipi_dsi_suspend(struct device *dev)
        if (client_drv && client_drv->suspend)
                client_drv->suspend(client_dev);
 
-       /* enable MIPI-DSI PHY. */
-       if (dsim->pd->phy_enable)
-               dsim->pd->phy_enable(pdev, false);
+       /* disable MIPI-DSI PHY. */
+       phy_power_off(dsim->phy);
 
        clk_disable(dsim->clock);
 
@@ -536,8 +538,7 @@ static int exynos_mipi_dsi_resume(struct device *dev)
        exynos_mipi_regulator_enable(dsim);
 
        /* enable MIPI-DSI PHY. */
-       if (dsim->pd->phy_enable)
-               dsim->pd->phy_enable(pdev, true);
+       phy_power_on(dsim->phy);
 
        clk_enable(dsim->clock);
 
index ee59b74768d9555e27802e0256a9d448f599f48d..fed0ce198ae3eacf76d747948f54cb051bda8d57 100644 (file)
@@ -13,18 +13,24 @@ static ssize_t device_show(struct device *_d,
        struct virtio_device *dev = dev_to_virtio(_d);
        return sprintf(buf, "0x%04x\n", dev->id.device);
 }
+static DEVICE_ATTR_RO(device);
+
 static ssize_t vendor_show(struct device *_d,
                           struct device_attribute *attr, char *buf)
 {
        struct virtio_device *dev = dev_to_virtio(_d);
        return sprintf(buf, "0x%04x\n", dev->id.vendor);
 }
+static DEVICE_ATTR_RO(vendor);
+
 static ssize_t status_show(struct device *_d,
                           struct device_attribute *attr, char *buf)
 {
        struct virtio_device *dev = dev_to_virtio(_d);
        return sprintf(buf, "0x%08x\n", dev->config->get_status(dev));
 }
+static DEVICE_ATTR_RO(status);
+
 static ssize_t modalias_show(struct device *_d,
                             struct device_attribute *attr, char *buf)
 {
@@ -32,6 +38,8 @@ static ssize_t modalias_show(struct device *_d,
        return sprintf(buf, "virtio:d%08Xv%08X\n",
                       dev->id.device, dev->id.vendor);
 }
+static DEVICE_ATTR_RO(modalias);
+
 static ssize_t features_show(struct device *_d,
                             struct device_attribute *attr, char *buf)
 {
@@ -47,14 +55,17 @@ static ssize_t features_show(struct device *_d,
        len += sprintf(buf+len, "\n");
        return len;
 }
-static struct device_attribute virtio_dev_attrs[] = {
-       __ATTR_RO(device),
-       __ATTR_RO(vendor),
-       __ATTR_RO(status),
-       __ATTR_RO(modalias),
-       __ATTR_RO(features),
-       __ATTR_NULL
+static DEVICE_ATTR_RO(features);
+
+static struct attribute *virtio_dev_attrs[] = {
+       &dev_attr_device.attr,
+       &dev_attr_vendor.attr,
+       &dev_attr_status.attr,
+       &dev_attr_modalias.attr,
+       &dev_attr_features.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(virtio_dev);
 
 static inline int virtio_id_match(const struct virtio_device *dev,
                                  const struct virtio_device_id *id)
@@ -165,7 +176,7 @@ static int virtio_dev_remove(struct device *_d)
 static struct bus_type virtio_bus = {
        .name  = "virtio",
        .match = virtio_dev_match,
-       .dev_attrs = virtio_dev_attrs,
+       .dev_groups = virtio_dev_groups,
        .uevent = virtio_uevent,
        .probe = virtio_dev_probe,
        .remove = virtio_dev_remove,
index 96cab6ac2b4e6fdda01cff9d5bdb7d185fb1f82f..41613f92a723448c1b4091a7e357fc05b2af9638 100644 (file)
@@ -498,7 +498,7 @@ static int ds1wm_probe(struct platform_device *pdev)
                irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING);
 
        ret = devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr,
-                       IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data);
+                       IRQF_SHARED, "ds1wm", ds1wm_data);
        if (ret)
                return ret;
 
index 6e94d8dd3d00a31d696fc7e140329d9b4dc72c6b..9900e8ec73939fef918abff3bda6829e6b25b4d3 100644 (file)
@@ -577,8 +577,7 @@ static int omap_hdq_probe(struct platform_device *pdev)
                goto err_irq;
        }
 
-       ret = devm_request_irq(dev, irq, hdq_isr, IRQF_DISABLED,
-                       "omap_hdq", hdq_data);
+       ret = devm_request_irq(dev, irq, hdq_isr, 0, "omap_hdq", hdq_data);
        if (ret < 0) {
                dev_dbg(&pdev->dev, "could not request irq\n");
                goto err_irq;
index f54ece268c982d1dce13224c78efce9434dbffbb..264ad1c583abcc78ad04a8190edfffcfc4aacde5 100644 (file)
@@ -58,6 +58,7 @@ static int w1_gpio_probe_dt(struct platform_device *pdev)
 {
        struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
        struct device_node *np = pdev->dev.of_node;
+       int gpio;
 
        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
@@ -66,7 +67,11 @@ static int w1_gpio_probe_dt(struct platform_device *pdev)
        if (of_get_property(np, "linux,open-drain", NULL))
                pdata->is_open_drain = 1;
 
-       pdata->pin = of_get_gpio(np, 0);
+       gpio = of_get_gpio(np, 0);
+       if (gpio < 0)
+               return gpio;
+       pdata->pin = gpio;
+
        pdata->ext_pullup_enable_pin = of_get_gpio(np, 1);
        pdev->dev.platform_data = pdata;
 
@@ -94,25 +99,27 @@ static int w1_gpio_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
-       master = kzalloc(sizeof(struct w1_bus_master), GFP_KERNEL);
+       master = devm_kzalloc(&pdev->dev, sizeof(struct w1_bus_master),
+                       GFP_KERNEL);
        if (!master) {
                dev_err(&pdev->dev, "Out of memory\n");
                return -ENOMEM;
        }
 
-       err = gpio_request(pdata->pin, "w1");
+       err = devm_gpio_request(&pdev->dev, pdata->pin, "w1");
        if (err) {
                dev_err(&pdev->dev, "gpio_request (pin) failed\n");
-               goto free_master;
+               return err;
        }
 
        if (gpio_is_valid(pdata->ext_pullup_enable_pin)) {
-               err = gpio_request_one(pdata->ext_pullup_enable_pin,
-                                      GPIOF_INIT_LOW, "w1 pullup");
+               err = devm_gpio_request_one(&pdev->dev,
+                               pdata->ext_pullup_enable_pin, GPIOF_INIT_LOW,
+                               "w1 pullup");
                if (err < 0) {
                        dev_err(&pdev->dev, "gpio_request_one "
                                        "(ext_pullup_enable_pin) failed\n");
-                       goto free_gpio;
+                       return err;
                }
        }
 
@@ -130,7 +137,7 @@ static int w1_gpio_probe(struct platform_device *pdev)
        err = w1_add_master_device(master);
        if (err) {
                dev_err(&pdev->dev, "w1_add_master device failed\n");
-               goto free_gpio_ext_pu;
+               return err;
        }
 
        if (pdata->enable_external_pullup)
@@ -142,16 +149,6 @@ static int w1_gpio_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, master);
 
        return 0;
-
- free_gpio_ext_pu:
-       if (gpio_is_valid(pdata->ext_pullup_enable_pin))
-               gpio_free(pdata->ext_pullup_enable_pin);
- free_gpio:
-       gpio_free(pdata->pin);
- free_master:
-       kfree(master);
-
-       return err;
 }
 
 static int w1_gpio_remove(struct platform_device *pdev)
@@ -166,8 +163,6 @@ static int w1_gpio_remove(struct platform_device *pdev)
                gpio_set_value(pdata->ext_pullup_enable_pin, 0);
 
        w1_remove_master_device(master);
-       gpio_free(pdata->pin);
-       kfree(master);
 
        return 0;
 }
index 38e92b770e91f224209dab2fc0e5543e8591bb65..3c0a74b3e9b15af5355c8619294805f59030f75d 100644 (file)
@@ -384,12 +384,14 @@ static ssize_t nodename_show(struct device *dev,
 {
        return sprintf(buf, "%s\n", to_xenbus_device(dev)->nodename);
 }
+static DEVICE_ATTR_RO(nodename);
 
 static ssize_t devtype_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
        return sprintf(buf, "%s\n", to_xenbus_device(dev)->devicetype);
 }
+static DEVICE_ATTR_RO(devtype);
 
 static ssize_t modalias_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
@@ -397,14 +399,24 @@ static ssize_t modalias_show(struct device *dev,
        return sprintf(buf, "%s:%s\n", dev->bus->name,
                       to_xenbus_device(dev)->devicetype);
 }
+static DEVICE_ATTR_RO(modalias);
 
-struct device_attribute xenbus_dev_attrs[] = {
-       __ATTR_RO(nodename),
-       __ATTR_RO(devtype),
-       __ATTR_RO(modalias),
-       __ATTR_NULL
+static struct attribute *xenbus_dev_attrs[] = {
+       &dev_attr_nodename.attr,
+       &dev_attr_devtype.attr,
+       &dev_attr_modalias.attr,
+       NULL,
 };
-EXPORT_SYMBOL_GPL(xenbus_dev_attrs);
+
+static const struct attribute_group xenbus_dev_group = {
+       .attrs = xenbus_dev_attrs,
+};
+
+const struct attribute_group *xenbus_dev_groups[] = {
+       &xenbus_dev_group,
+       NULL,
+};
+EXPORT_SYMBOL_GPL(xenbus_dev_groups);
 
 int xenbus_probe_node(struct xen_bus_type *bus,
                      const char *type,
index 146f857a36f83b5a2f4646424c1251a23abf1b07..1085ec294a1987a5620b24c4a2e120528d13cac0 100644 (file)
@@ -54,7 +54,7 @@ enum xenstore_init {
        XS_LOCAL,
 };
 
-extern struct device_attribute xenbus_dev_attrs[];
+extern const struct attribute_group *xenbus_dev_groups[];
 
 extern int xenbus_match(struct device *_dev, struct device_driver *_drv);
 extern int xenbus_dev_probe(struct device *_dev);
index 998bbbab816be6c59a4a71b8b183a2a39f053aff..5125dce11a6083da92fc5942a09c2aa786f181d8 100644 (file)
@@ -200,7 +200,7 @@ static struct xen_bus_type xenbus_backend = {
                .probe          = xenbus_dev_probe,
                .remove         = xenbus_dev_remove,
                .shutdown       = xenbus_dev_shutdown,
-               .dev_attrs      = xenbus_dev_attrs,
+               .dev_groups     = xenbus_dev_groups,
        },
 };
 
index 34b20bfa4e8c3cf813194a45c4545e01c1d88eb1..129bf84c19ec999f5e239c1b7168089955a29bec 100644 (file)
@@ -154,7 +154,7 @@ static struct xen_bus_type xenbus_frontend = {
                .probe          = xenbus_frontend_dev_probe,
                .remove         = xenbus_dev_remove,
                .shutdown       = xenbus_dev_shutdown,
-               .dev_attrs      = xenbus_dev_attrs,
+               .dev_groups     = xenbus_dev_groups,
 
                .pm             = &xenbus_pm_ops,
        },
index 41000305d716ea51c47ed52ddb5abe024045e958..ae6ebb88ceff15ccef25f26804532d8b89cbe45a 100644 (file)
@@ -542,7 +542,7 @@ EXPORT_SYMBOL(d_drop);
  * If ref is non-zero, then decrement the refcount too.
  * Returns dentry requiring refcount drop, or NULL if we're done.
  */
-static inline struct dentry *
+static struct dentry *
 dentry_kill(struct dentry *dentry, int unlock_on_failure)
        __releases(dentry->d_lock)
 {
@@ -630,7 +630,8 @@ repeat:
                        goto kill_it;
        }
 
-       dentry->d_flags |= DCACHE_REFERENCED;
+       if (!(dentry->d_flags & DCACHE_REFERENCED))
+               dentry->d_flags |= DCACHE_REFERENCED;
        dentry_lru_add(dentry);
 
        dentry->d_lockref.count--;
@@ -1331,14 +1332,6 @@ rename_retry:
  * list is non-empty and continue searching.
  */
 
-/**
- * have_submounts - check for mounts over a dentry
- * @parent: dentry to check.
- *
- * Return true if the parent or its subdirectories contain
- * a mount point
- */
-
 static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
 {
        int *ret = data;
@@ -1349,6 +1342,13 @@ static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
        return D_WALK_CONTINUE;
 }
 
+/**
+ * have_submounts - check for mounts over a dentry
+ * @parent: dentry to check.
+ *
+ * Return true if the parent or its subdirectories contain
+ * a mount point
+ */
 int have_submounts(struct dentry *parent)
 {
        int ret = 0;
index c88e355f7635f61e59f58cb4acc617b84928e4e5..000eae2782b6e905aefa05980ef91c54b81a8274 100644 (file)
@@ -408,7 +408,7 @@ static loff_t lower_offset_for_page(struct ecryptfs_crypt_stat *crypt_stat,
                                    struct page *page)
 {
        return ecryptfs_lower_header_size(crypt_stat) +
-              (page->index << PAGE_CACHE_SHIFT);
+              ((loff_t)page->index << PAGE_CACHE_SHIFT);
 }
 
 /**
index 7d52806c21197206a5932b08a68e7dc9d6899253..4725a07f003cf3279fa81813748442afc24a053c 100644 (file)
@@ -1149,7 +1149,7 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok,
        struct ecryptfs_msg_ctx *msg_ctx;
        struct ecryptfs_message *msg = NULL;
        char *auth_tok_sig;
-       char *payload;
+       char *payload = NULL;
        size_t payload_len = 0;
        int rc;
 
@@ -1203,6 +1203,7 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok,
        }
 out:
        kfree(msg);
+       kfree(payload);
        return rc;
 }
 
index 473e09da7d02d3396273221f3094824c91f3b030..810c28fb8c3c764dd5a3162ad3fd4239a4433a55 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/mutex.h>
 #include <linux/anon_inodes.h>
 #include <linux/device.h>
-#include <linux/freezer.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/mman.h>
@@ -1605,8 +1604,7 @@ fetch_events:
                        }
 
                        spin_unlock_irqrestore(&ep->lock, flags);
-                       if (!freezable_schedule_hrtimeout_range(to, slack,
-                                                               HRTIMER_MODE_ABS))
+                       if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
                                timed_out = 1;
 
                        spin_lock_irqsave(&ep->lock, flags);
index abdd15ad13c9c52bf672465787e3811c06ef7224..e900ca518635bcecb1cfdbe9d54cab640ec49333 100644 (file)
@@ -297,7 +297,7 @@ void flush_delayed_fput(void)
        delayed_fput(NULL);
 }
 
-static DECLARE_WORK(delayed_fput_work, delayed_fput);
+static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
 
 void fput(struct file *file)
 {
@@ -317,7 +317,7 @@ void fput(struct file *file)
                }
 
                if (llist_add(&file->f_u.fu_llist, &delayed_fput_list))
-                       schedule_work(&delayed_fput_work);
+                       schedule_delayed_work(&delayed_fput_work, 1);
        }
 }
 
index c1a3e603279c9cbe4fb141fc2b9bdcfa1d76a033..7f464c513ba0a85a2fd4fe7923a9799bb319e92b 100644 (file)
@@ -95,7 +95,7 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
 
        if (insert_inode_locked(inode) < 0) {
                rc = -EINVAL;
-               goto fail_unlock;
+               goto fail_put;
        }
 
        inode_init_owner(inode, parent, mode);
@@ -156,7 +156,6 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
 fail_drop:
        dquot_drop(inode);
        inode->i_flags |= S_NOQUOTA;
-fail_unlock:
        clear_nlink(inode);
        unlock_new_inode(inode);
 fail_put:
index 645268f23eb64cb8c2931ce33391beb2d0080c36..caa28051e197e898e3c2bc52afce37bcbb284853 100644 (file)
@@ -2294,10 +2294,11 @@ out:
  * path_mountpoint - look up a path to be umounted
  * @dfd:       directory file descriptor to start walk from
  * @name:      full pathname to walk
+ * @path:      pointer to container for result
  * @flags:     lookup flags
  *
  * Look up the given name, but don't attempt to revalidate the last component.
- * Returns 0 and "path" will be valid on success; Retuns error otherwise.
+ * Returns 0 and "path" will be valid on success; Returns error otherwise.
  */
 static int
 path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags)
index 35d4adc749d9616f4bb2a7cd6d952eb18b5579b4..dfd5cb18c0127faeea806a29ef090ee14affc87e 100644 (file)
@@ -238,8 +238,7 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
 
        set_current_state(state);
        if (!pwq->triggered)
-               rc = freezable_schedule_hrtimeout_range(expires, slack,
-                                                       HRTIMER_MODE_ABS);
+               rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS);
        __set_current_state(TASK_RUNNING);
 
        /*
index 3135c2525c76635606ae9722f6badb5a11e44543..a290157265ef5cfb52dbdb6a9e950e9733462d16 100644 (file)
@@ -328,6 +328,8 @@ loff_t seq_lseek(struct file *file, loff_t offset, int whence)
                                m->read_pos = offset;
                                retval = file->f_pos = offset;
                        }
+               } else {
+                       file->f_pos = offset;
                }
        }
        file->f_version = m->version;
index 7a1ceb946b80968636c201f7bfe618b691571898..8876ac18337319b16f0da09ea40a52f5c642478f 100644 (file)
@@ -2,5 +2,4 @@
 # Makefile for the sysfs virtual filesystem
 #
 
-obj-y          := inode.o file.o dir.o symlink.o mount.o bin.o \
-                  group.o
+obj-y          := inode.o file.o dir.o symlink.o mount.o group.o
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
deleted file mode 100644 (file)
index c590cab..0000000
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * fs/sysfs/bin.c - sysfs binary file implementation
- *
- * Copyright (c) 2003 Patrick Mochel
- * Copyright (c) 2003 Matthew Wilcox
- * Copyright (c) 2004 Silicon Graphics, Inc.
- * Copyright (c) 2007 SUSE Linux Products GmbH
- * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
- *
- * This file is released under the GPLv2.
- *
- * Please see Documentation/filesystems/sysfs.txt for more information.
- */
-
-#undef DEBUG
-
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/kobject.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/mm.h>
-#include <linux/uaccess.h>
-
-#include "sysfs.h"
-
-/*
- * There's one bin_buffer for each open file.
- *
- * filp->private_data points to bin_buffer and
- * sysfs_dirent->s_bin_attr.buffers points to a the bin_buffer s
- * sysfs_dirent->s_bin_attr.buffers is protected by sysfs_bin_lock
- */
-static DEFINE_MUTEX(sysfs_bin_lock);
-
-struct bin_buffer {
-       struct mutex                    mutex;
-       void                            *buffer;
-       int                             mmapped;
-       const struct vm_operations_struct *vm_ops;
-       struct file                     *file;
-       struct hlist_node               list;
-};
-
-static int
-fill_read(struct file *file, char *buffer, loff_t off, size_t count)
-{
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
-       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
-       int rc;
-
-       /* need attr_sd for attr, its parent for kobj */
-       if (!sysfs_get_active(attr_sd))
-               return -ENODEV;
-
-       rc = -EIO;
-       if (attr->read)
-               rc = attr->read(file, kobj, attr, buffer, off, count);
-
-       sysfs_put_active(attr_sd);
-
-       return rc;
-}
-
-static ssize_t
-read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
-{
-       struct bin_buffer *bb = file->private_data;
-       int size = file_inode(file)->i_size;
-       loff_t offs = *off;
-       int count = min_t(size_t, bytes, PAGE_SIZE);
-       char *temp;
-
-       if (!bytes)
-               return 0;
-
-       if (size) {
-               if (offs > size)
-                       return 0;
-               if (offs + count > size)
-                       count = size - offs;
-       }
-
-       temp = kmalloc(count, GFP_KERNEL);
-       if (!temp)
-               return -ENOMEM;
-
-       mutex_lock(&bb->mutex);
-
-       count = fill_read(file, bb->buffer, offs, count);
-       if (count < 0) {
-               mutex_unlock(&bb->mutex);
-               goto out_free;
-       }
-
-       memcpy(temp, bb->buffer, count);
-
-       mutex_unlock(&bb->mutex);
-
-       if (copy_to_user(userbuf, temp, count)) {
-               count = -EFAULT;
-               goto out_free;
-       }
-
-       pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
-
-       *off = offs + count;
-
- out_free:
-       kfree(temp);
-       return count;
-}
-
-static int
-flush_write(struct file *file, char *buffer, loff_t offset, size_t count)
-{
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
-       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
-       int rc;
-
-       /* need attr_sd for attr, its parent for kobj */
-       if (!sysfs_get_active(attr_sd))
-               return -ENODEV;
-
-       rc = -EIO;
-       if (attr->write)
-               rc = attr->write(file, kobj, attr, buffer, offset, count);
-
-       sysfs_put_active(attr_sd);
-
-       return rc;
-}
-
-static ssize_t write(struct file *file, const char __user *userbuf,
-                    size_t bytes, loff_t *off)
-{
-       struct bin_buffer *bb = file->private_data;
-       int size = file_inode(file)->i_size;
-       loff_t offs = *off;
-       int count = min_t(size_t, bytes, PAGE_SIZE);
-       char *temp;
-
-       if (!bytes)
-               return 0;
-
-       if (size) {
-               if (offs > size)
-                       return 0;
-               if (offs + count > size)
-                       count = size - offs;
-       }
-
-       temp = memdup_user(userbuf, count);
-       if (IS_ERR(temp))
-               return PTR_ERR(temp);
-
-       mutex_lock(&bb->mutex);
-
-       memcpy(bb->buffer, temp, count);
-
-       count = flush_write(file, bb->buffer, offs, count);
-       mutex_unlock(&bb->mutex);
-
-       if (count > 0)
-               *off = offs + count;
-
-       kfree(temp);
-       return count;
-}
-
-static void bin_vma_open(struct vm_area_struct *vma)
-{
-       struct file *file = vma->vm_file;
-       struct bin_buffer *bb = file->private_data;
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-
-       if (!bb->vm_ops)
-               return;
-
-       if (!sysfs_get_active(attr_sd))
-               return;
-
-       if (bb->vm_ops->open)
-               bb->vm_ops->open(vma);
-
-       sysfs_put_active(attr_sd);
-}
-
-static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
-{
-       struct file *file = vma->vm_file;
-       struct bin_buffer *bb = file->private_data;
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       int ret;
-
-       if (!bb->vm_ops)
-               return VM_FAULT_SIGBUS;
-
-       if (!sysfs_get_active(attr_sd))
-               return VM_FAULT_SIGBUS;
-
-       ret = VM_FAULT_SIGBUS;
-       if (bb->vm_ops->fault)
-               ret = bb->vm_ops->fault(vma, vmf);
-
-       sysfs_put_active(attr_sd);
-       return ret;
-}
-
-static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
-{
-       struct file *file = vma->vm_file;
-       struct bin_buffer *bb = file->private_data;
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       int ret;
-
-       if (!bb->vm_ops)
-               return VM_FAULT_SIGBUS;
-
-       if (!sysfs_get_active(attr_sd))
-               return VM_FAULT_SIGBUS;
-
-       ret = 0;
-       if (bb->vm_ops->page_mkwrite)
-               ret = bb->vm_ops->page_mkwrite(vma, vmf);
-       else
-               file_update_time(file);
-
-       sysfs_put_active(attr_sd);
-       return ret;
-}
-
-static int bin_access(struct vm_area_struct *vma, unsigned long addr,
-                 void *buf, int len, int write)
-{
-       struct file *file = vma->vm_file;
-       struct bin_buffer *bb = file->private_data;
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       int ret;
-
-       if (!bb->vm_ops)
-               return -EINVAL;
-
-       if (!sysfs_get_active(attr_sd))
-               return -EINVAL;
-
-       ret = -EINVAL;
-       if (bb->vm_ops->access)
-               ret = bb->vm_ops->access(vma, addr, buf, len, write);
-
-       sysfs_put_active(attr_sd);
-       return ret;
-}
-
-#ifdef CONFIG_NUMA
-static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
-{
-       struct file *file = vma->vm_file;
-       struct bin_buffer *bb = file->private_data;
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       int ret;
-
-       if (!bb->vm_ops)
-               return 0;
-
-       if (!sysfs_get_active(attr_sd))
-               return -EINVAL;
-
-       ret = 0;
-       if (bb->vm_ops->set_policy)
-               ret = bb->vm_ops->set_policy(vma, new);
-
-       sysfs_put_active(attr_sd);
-       return ret;
-}
-
-static struct mempolicy *bin_get_policy(struct vm_area_struct *vma,
-                                       unsigned long addr)
-{
-       struct file *file = vma->vm_file;
-       struct bin_buffer *bb = file->private_data;
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       struct mempolicy *pol;
-
-       if (!bb->vm_ops)
-               return vma->vm_policy;
-
-       if (!sysfs_get_active(attr_sd))
-               return vma->vm_policy;
-
-       pol = vma->vm_policy;
-       if (bb->vm_ops->get_policy)
-               pol = bb->vm_ops->get_policy(vma, addr);
-
-       sysfs_put_active(attr_sd);
-       return pol;
-}
-
-static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from,
-                       const nodemask_t *to, unsigned long flags)
-{
-       struct file *file = vma->vm_file;
-       struct bin_buffer *bb = file->private_data;
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       int ret;
-
-       if (!bb->vm_ops)
-               return 0;
-
-       if (!sysfs_get_active(attr_sd))
-               return 0;
-
-       ret = 0;
-       if (bb->vm_ops->migrate)
-               ret = bb->vm_ops->migrate(vma, from, to, flags);
-
-       sysfs_put_active(attr_sd);
-       return ret;
-}
-#endif
-
-static const struct vm_operations_struct bin_vm_ops = {
-       .open           = bin_vma_open,
-       .fault          = bin_fault,
-       .page_mkwrite   = bin_page_mkwrite,
-       .access         = bin_access,
-#ifdef CONFIG_NUMA
-       .set_policy     = bin_set_policy,
-       .get_policy     = bin_get_policy,
-       .migrate        = bin_migrate,
-#endif
-};
-
-static int mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct bin_buffer *bb = file->private_data;
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
-       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
-       int rc;
-
-       mutex_lock(&bb->mutex);
-
-       /* need attr_sd for attr, its parent for kobj */
-       rc = -ENODEV;
-       if (!sysfs_get_active(attr_sd))
-               goto out_unlock;
-
-       rc = -EINVAL;
-       if (!attr->mmap)
-               goto out_put;
-
-       rc = attr->mmap(file, kobj, attr, vma);
-       if (rc)
-               goto out_put;
-
-       /*
-        * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup()
-        * to satisfy versions of X which crash if the mmap fails: that
-        * substitutes a new vm_file, and we don't then want bin_vm_ops.
-        */
-       if (vma->vm_file != file)
-               goto out_put;
-
-       rc = -EINVAL;
-       if (bb->mmapped && bb->vm_ops != vma->vm_ops)
-               goto out_put;
-
-       /*
-        * It is not possible to successfully wrap close.
-        * So error if someone is trying to use close.
-        */
-       rc = -EINVAL;
-       if (vma->vm_ops && vma->vm_ops->close)
-               goto out_put;
-
-       rc = 0;
-       bb->mmapped = 1;
-       bb->vm_ops = vma->vm_ops;
-       vma->vm_ops = &bin_vm_ops;
-out_put:
-       sysfs_put_active(attr_sd);
-out_unlock:
-       mutex_unlock(&bb->mutex);
-
-       return rc;
-}
-
-static int open(struct inode *inode, struct file *file)
-{
-       struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
-       struct bin_buffer *bb = NULL;
-       int error;
-
-       /* binary file operations requires both @sd and its parent */
-       if (!sysfs_get_active(attr_sd))
-               return -ENODEV;
-
-       error = -EACCES;
-       if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
-               goto err_out;
-       if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
-               goto err_out;
-
-       error = -ENOMEM;
-       bb = kzalloc(sizeof(*bb), GFP_KERNEL);
-       if (!bb)
-               goto err_out;
-
-       bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
-       if (!bb->buffer)
-               goto err_out;
-
-       mutex_init(&bb->mutex);
-       bb->file = file;
-       file->private_data = bb;
-
-       mutex_lock(&sysfs_bin_lock);
-       hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers);
-       mutex_unlock(&sysfs_bin_lock);
-
-       /* open succeeded, put active references */
-       sysfs_put_active(attr_sd);
-       return 0;
-
- err_out:
-       sysfs_put_active(attr_sd);
-       kfree(bb);
-       return error;
-}
-
-static int release(struct inode *inode, struct file *file)
-{
-       struct bin_buffer *bb = file->private_data;
-
-       mutex_lock(&sysfs_bin_lock);
-       hlist_del(&bb->list);
-       mutex_unlock(&sysfs_bin_lock);
-
-       kfree(bb->buffer);
-       kfree(bb);
-       return 0;
-}
-
-const struct file_operations bin_fops = {
-       .read           = read,
-       .write          = write,
-       .mmap           = mmap,
-       .llseek         = generic_file_llseek,
-       .open           = open,
-       .release        = release,
-};
-
-
-void unmap_bin_file(struct sysfs_dirent *attr_sd)
-{
-       struct bin_buffer *bb;
-
-       if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR)
-               return;
-
-       mutex_lock(&sysfs_bin_lock);
-
-       hlist_for_each_entry(bb, &attr_sd->s_bin_attr.buffers, list) {
-               struct inode *inode = file_inode(bb->file);
-
-               unmap_mapping_range(inode->i_mapping, 0, 0, 1);
-       }
-
-       mutex_unlock(&sysfs_bin_lock);
-}
-
-/**
- *     sysfs_create_bin_file - create binary file for object.
- *     @kobj:  object.
- *     @attr:  attribute descriptor.
- */
-int sysfs_create_bin_file(struct kobject *kobj,
-                         const struct bin_attribute *attr)
-{
-       BUG_ON(!kobj || !kobj->sd || !attr);
-
-       return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
-}
-EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
-
-/**
- *     sysfs_remove_bin_file - remove binary file for object.
- *     @kobj:  object.
- *     @attr:  attribute descriptor.
- */
-void sysfs_remove_bin_file(struct kobject *kobj,
-                          const struct bin_attribute *attr)
-{
-       sysfs_hash_and_remove(kobj->sd, NULL, attr->attr.name);
-}
-EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
index 4d83cedb9fcb6a98b784bb7e5d67472b9e96e289..08c66969d52a7af81071cbdf2dfc1a7a81dcad98 100644 (file)
 #include "sysfs.h"
 
 DEFINE_MUTEX(sysfs_mutex);
-DEFINE_SPINLOCK(sysfs_assoc_lock);
+DEFINE_SPINLOCK(sysfs_symlink_target_lock);
 
-#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb);
+#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb)
 
 static DEFINE_SPINLOCK(sysfs_ino_lock);
 static DEFINE_IDA(sysfs_ino_ida);
 
 /**
  *     sysfs_name_hash
- *     @ns:   Namespace tag to hash
  *     @name: Null terminated string to hash
+ *     @ns:   Namespace tag to hash
  *
  *     Returns 31 bit hash of ns + name (so it fits in an off_t )
  */
-static unsigned int sysfs_name_hash(const void *ns, const char *name)
+static unsigned int sysfs_name_hash(const char *name, const void *ns)
 {
        unsigned long hash = init_name_hash();
        unsigned int len = strlen(name);
@@ -56,8 +56,8 @@ static unsigned int sysfs_name_hash(const void *ns, const char *name)
        return hash;
 }
 
-static int sysfs_name_compare(unsigned int hash, const void *ns,
-       const char *name, const struct sysfs_dirent *sd)
+static int sysfs_name_compare(unsigned int hash, const char *name,
+                             const void *ns, const struct sysfs_dirent *sd)
 {
        if (hash != sd->s_hash)
                return hash - sd->s_hash;
@@ -69,7 +69,7 @@ static int sysfs_name_compare(unsigned int hash, const void *ns,
 static int sysfs_sd_compare(const struct sysfs_dirent *left,
                            const struct sysfs_dirent *right)
 {
-       return sysfs_name_compare(left->s_hash, left->s_ns, left->s_name,
+       return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns,
                                  right);
 }
 
@@ -111,6 +111,11 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd)
        /* add new node and rebalance the tree */
        rb_link_node(&sd->s_rb, parent, node);
        rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children);
+
+       /* if @sd has ns tag, mark the parent to enable ns filtering */
+       if (sd->s_ns)
+               sd->s_parent->s_flags |= SYSFS_FLAG_HAS_NS;
+
        return 0;
 }
 
@@ -130,26 +135,15 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
                sd->s_parent->s_dir.subdirs--;
 
        rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
-}
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
 
-/* Test for attributes that want to ignore lockdep for read-locking */
-static bool ignore_lockdep(struct sysfs_dirent *sd)
-{
-       return sysfs_type(sd) == SYSFS_KOBJ_ATTR &&
-                       sd->s_attr.attr->ignore_lockdep;
-}
-
-#else
-
-static inline bool ignore_lockdep(struct sysfs_dirent *sd)
-{
-       return true;
+       /*
+        * Either all or none of the children have tags.  Clearing HAS_NS
+        * when there's no child left is enough to keep the flag synced.
+        */
+       if (RB_EMPTY_ROOT(&sd->s_parent->s_dir.children))
+               sd->s_parent->s_flags &= ~SYSFS_FLAG_HAS_NS;
 }
 
-#endif
-
 /**
  *     sysfs_get_active - get an active reference to sysfs_dirent
  *     @sd: sysfs_dirent to get an active reference to
@@ -168,7 +162,7 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
        if (!atomic_inc_unless_negative(&sd->s_active))
                return NULL;
 
-       if (likely(!ignore_lockdep(sd)))
+       if (likely(!sysfs_ignore_lockdep(sd)))
                rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
        return sd;
 }
@@ -187,7 +181,7 @@ void sysfs_put_active(struct sysfs_dirent *sd)
        if (unlikely(!sd))
                return;
 
-       if (likely(!ignore_lockdep(sd)))
+       if (likely(!sysfs_ignore_lockdep(sd)))
                rwsem_release(&sd->dep_map, 1, _RET_IP_);
        v = atomic_dec_return(&sd->s_active);
        if (likely(v != SD_DEACTIVATED_BIAS))
@@ -297,7 +291,6 @@ static int sysfs_dentry_delete(const struct dentry *dentry)
 static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
 {
        struct sysfs_dirent *sd;
-       int type;
 
        if (flags & LOOKUP_RCU)
                return -ECHILD;
@@ -318,13 +311,8 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
                goto out_bad;
 
        /* The sysfs dirent has been moved to a different namespace */
-       type = KOBJ_NS_TYPE_NONE;
-       if (sd->s_parent) {
-               type = sysfs_ns_type(sd->s_parent);
-               if (type != KOBJ_NS_TYPE_NONE &&
-                               sysfs_info(dentry->d_sb)->ns[type] != sd->s_ns)
-                       goto out_bad;
-       }
+       if (sd->s_ns && sd->s_ns != sysfs_info(dentry->d_sb)->ns)
+               goto out_bad;
 
        mutex_unlock(&sysfs_mutex);
 out_valid:
@@ -400,22 +388,19 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
 /**
  *     sysfs_addrm_start - prepare for sysfs_dirent add/remove
  *     @acxt: pointer to sysfs_addrm_cxt to be used
- *     @parent_sd: parent sysfs_dirent
  *
- *     This function is called when the caller is about to add or
- *     remove sysfs_dirent under @parent_sd.  This function acquires
- *     sysfs_mutex.  @acxt is used to keep and pass context to
- *     other addrm functions.
+ *     This function is called when the caller is about to add or remove
+ *     sysfs_dirent.  This function acquires sysfs_mutex.  @acxt is used
+ *     to keep and pass context to other addrm functions.
  *
  *     LOCKING:
  *     Kernel thread context (may sleep).  sysfs_mutex is locked on
  *     return.
  */
-void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
-                      struct sysfs_dirent *parent_sd)
+void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt)
+       __acquires(sysfs_mutex)
 {
        memset(acxt, 0, sizeof(*acxt));
-       acxt->parent_sd = parent_sd;
 
        mutex_lock(&sysfs_mutex);
 }
@@ -424,10 +409,11 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
  *     __sysfs_add_one - add sysfs_dirent to parent without warning
  *     @acxt: addrm context to use
  *     @sd: sysfs_dirent to be added
+ *     @parent_sd: the parent sysfs_dirent to add @sd to
  *
- *     Get @acxt->parent_sd and set sd->s_parent to it and increment
- *     nlink of parent inode if @sd is a directory and link into the
- *     children list of the parent.
+ *     Get @parent_sd and set @sd->s_parent to it and increment nlink of
+ *     the parent inode if @sd is a directory and link into the children
+ *     list of the parent.
  *
  *     This function should be called between calls to
  *     sysfs_addrm_start() and sysfs_addrm_finish() and should be
@@ -440,27 +426,21 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
  *     0 on success, -EEXIST if entry with the given name already
  *     exists.
  */
-int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
+int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd,
+                   struct sysfs_dirent *parent_sd)
 {
        struct sysfs_inode_attrs *ps_iattr;
        int ret;
 
-       if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) {
-               WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
-                       sysfs_ns_type(acxt->parent_sd) ? "required" : "invalid",
-                       acxt->parent_sd->s_name, sd->s_name);
-               return -EINVAL;
-       }
-
-       sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
-       sd->s_parent = sysfs_get(acxt->parent_sd);
+       sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns);
+       sd->s_parent = sysfs_get(parent_sd);
 
        ret = sysfs_link_sibling(sd);
        if (ret)
                return ret;
 
        /* Update timestamps on the parent */
-       ps_iattr = acxt->parent_sd->s_iattr;
+       ps_iattr = parent_sd->s_iattr;
        if (ps_iattr) {
                struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
                ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
@@ -490,14 +470,32 @@ static char *sysfs_pathname(struct sysfs_dirent *sd, char *path)
        return path;
 }
 
+void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name)
+{
+       char *path;
+
+       path = kzalloc(PATH_MAX, GFP_KERNEL);
+       if (path) {
+               sysfs_pathname(parent, path);
+               strlcat(path, "/", PATH_MAX);
+               strlcat(path, name, PATH_MAX);
+       }
+
+       WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s'\n",
+            path ? path : name);
+
+       kfree(path);
+}
+
 /**
  *     sysfs_add_one - add sysfs_dirent to parent
  *     @acxt: addrm context to use
  *     @sd: sysfs_dirent to be added
+ *     @parent_sd: the parent sysfs_dirent to add @sd to
  *
- *     Get @acxt->parent_sd and set sd->s_parent to it and increment
- *     nlink of parent inode if @sd is a directory and link into the
- *     children list of the parent.
+ *     Get @parent_sd and set @sd->s_parent to it and increment nlink of
+ *     the parent inode if @sd is a directory and link into the children
+ *     list of the parent.
  *
  *     This function should be called between calls to
  *     sysfs_addrm_start() and sysfs_addrm_finish() and should be
@@ -510,23 +508,15 @@ static char *sysfs_pathname(struct sysfs_dirent *sd, char *path)
  *     0 on success, -EEXIST if entry with the given name already
  *     exists.
  */
-int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
+int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd,
+                 struct sysfs_dirent *parent_sd)
 {
        int ret;
 
-       ret = __sysfs_add_one(acxt, sd);
-       if (ret == -EEXIST) {
-               char *path = kzalloc(PATH_MAX, GFP_KERNEL);
-               WARN(1, KERN_WARNING
-                    "sysfs: cannot create duplicate filename '%s'\n",
-                    (path == NULL) ? sd->s_name
-                                   : (sysfs_pathname(acxt->parent_sd, path),
-                                      strlcat(path, "/", PATH_MAX),
-                                      strlcat(path, sd->s_name, PATH_MAX),
-                                      path));
-               kfree(path);
-       }
+       ret = __sysfs_add_one(acxt, sd, parent_sd);
 
+       if (ret == -EEXIST)
+               sysfs_warn_dup(parent_sd, sd->s_name);
        return ret;
 }
 
@@ -545,16 +535,22 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
  *     LOCKING:
  *     Determined by sysfs_addrm_start().
  */
-void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
+static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt,
+                            struct sysfs_dirent *sd)
 {
        struct sysfs_inode_attrs *ps_iattr;
 
-       BUG_ON(sd->s_flags & SYSFS_FLAG_REMOVED);
+       /*
+        * Removal can be called multiple times on the same node.  Only the
+        * first invocation is effective and puts the base ref.
+        */
+       if (sd->s_flags & SYSFS_FLAG_REMOVED)
+               return;
 
        sysfs_unlink_sibling(sd);
 
        /* Update timestamps on the parent */
-       ps_iattr = acxt->parent_sd->s_iattr;
+       ps_iattr = sd->s_parent->s_iattr;
        if (ps_iattr) {
                struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
                ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
@@ -577,6 +573,7 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
  *     sysfs_mutex is released.
  */
 void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
+       __releases(sysfs_mutex)
 {
        /* release resources acquired by sysfs_addrm_start() */
        mutex_unlock(&sysfs_mutex);
@@ -588,7 +585,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
                acxt->removed = sd->u.removed_list;
 
                sysfs_deactivate(sd);
-               unmap_bin_file(sd);
+               sysfs_unmap_bin_file(sd);
                sysfs_put(sd);
        }
 }
@@ -597,6 +594,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
  *     sysfs_find_dirent - find sysfs_dirent with the given name
  *     @parent_sd: sysfs_dirent to search under
  *     @name: name to look for
+ *     @ns: the namespace tag to use
  *
  *     Look for sysfs_dirent with name @name under @parent_sd.
  *
@@ -607,26 +605,19 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
  *     Pointer to sysfs_dirent if found, NULL if not.
  */
 struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
-                                      const void *ns,
-                                      const unsigned char *name)
+                                      const unsigned char *name,
+                                      const void *ns)
 {
        struct rb_node *node = parent_sd->s_dir.children.rb_node;
        unsigned int hash;
 
-       if (!!sysfs_ns_type(parent_sd) != !!ns) {
-               WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
-                       sysfs_ns_type(parent_sd) ? "required" : "invalid",
-                       parent_sd->s_name, name);
-               return NULL;
-       }
-
-       hash = sysfs_name_hash(ns, name);
+       hash = sysfs_name_hash(name, ns);
        while (node) {
                struct sysfs_dirent *sd;
                int result;
 
                sd = to_sysfs_dirent(node);
-               result = sysfs_name_compare(hash, ns, name, sd);
+               result = sysfs_name_compare(hash, name, ns, sd);
                if (result < 0)
                        node = node->rb_left;
                else if (result > 0)
@@ -638,9 +629,10 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
 }
 
 /**
- *     sysfs_get_dirent - find and get sysfs_dirent with the given name
+ *     sysfs_get_dirent_ns - find and get sysfs_dirent with the given name
  *     @parent_sd: sysfs_dirent to search under
  *     @name: name to look for
+ *     @ns: the namespace tag to use
  *
  *     Look for sysfs_dirent with name @name under @parent_sd and get
  *     it if found.
@@ -651,24 +643,24 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
  *     RETURNS:
  *     Pointer to sysfs_dirent if found, NULL if not.
  */
-struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
-                                     const void *ns,
-                                     const unsigned char *name)
+struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd,
+                                        const unsigned char *name,
+                                        const void *ns)
 {
        struct sysfs_dirent *sd;
 
        mutex_lock(&sysfs_mutex);
-       sd = sysfs_find_dirent(parent_sd, ns, name);
+       sd = sysfs_find_dirent(parent_sd, name, ns);
        sysfs_get(sd);
        mutex_unlock(&sysfs_mutex);
 
        return sd;
 }
-EXPORT_SYMBOL_GPL(sysfs_get_dirent);
+EXPORT_SYMBOL_GPL(sysfs_get_dirent_ns);
 
 static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
-       enum kobj_ns_type type, const void *ns, const char *name,
-       struct sysfs_dirent **p_sd)
+                     const char *name, const void *ns,
+                     struct sysfs_dirent **p_sd)
 {
        umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
        struct sysfs_addrm_cxt acxt;
@@ -680,13 +672,12 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
        if (!sd)
                return -ENOMEM;
 
-       sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT);
        sd->s_ns = ns;
        sd->s_dir.kobj = kobj;
 
        /* link in */
-       sysfs_addrm_start(&acxt, parent_sd);
-       rc = sysfs_add_one(&acxt, sd);
+       sysfs_addrm_start(&acxt);
+       rc = sysfs_add_one(&acxt, sd, parent_sd);
        sysfs_addrm_finish(&acxt);
 
        if (rc == 0)
@@ -700,44 +691,17 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
 int sysfs_create_subdir(struct kobject *kobj, const char *name,
                        struct sysfs_dirent **p_sd)
 {
-       return create_dir(kobj, kobj->sd,
-                         KOBJ_NS_TYPE_NONE, NULL, name, p_sd);
+       return create_dir(kobj, kobj->sd, name, NULL, p_sd);
 }
 
 /**
- *     sysfs_read_ns_type: return associated ns_type
- *     @kobj: the kobject being queried
- *
- *     Each kobject can be tagged with exactly one namespace type
- *     (i.e. network or user).  Return the ns_type associated with
- *     this object if any
+ * sysfs_create_dir_ns - create a directory for an object with a namespace tag
+ * @kobj: object we're creating directory for
+ * @ns: the namespace tag to use
  */
-static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj)
+int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
 {
-       const struct kobj_ns_type_operations *ops;
-       enum kobj_ns_type type;
-
-       ops = kobj_child_ns_ops(kobj);
-       if (!ops)
-               return KOBJ_NS_TYPE_NONE;
-
-       type = ops->type;
-       BUG_ON(type <= KOBJ_NS_TYPE_NONE);
-       BUG_ON(type >= KOBJ_NS_TYPES);
-       BUG_ON(!kobj_ns_type_registered(type));
-
-       return type;
-}
-
-/**
- *     sysfs_create_dir - create a directory for an object.
- *     @kobj:          object we're creating directory for.
- */
-int sysfs_create_dir(struct kobject *kobj)
-{
-       enum kobj_ns_type type;
        struct sysfs_dirent *parent_sd, *sd;
-       const void *ns = NULL;
        int error = 0;
 
        BUG_ON(!kobj);
@@ -750,11 +714,7 @@ int sysfs_create_dir(struct kobject *kobj)
        if (!parent_sd)
                return -ENOENT;
 
-       if (sysfs_ns_type(parent_sd))
-               ns = kobj->ktype->namespace(kobj);
-       type = sysfs_read_ns_type(kobj);
-
-       error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
+       error = create_dir(kobj, parent_sd, kobject_name(kobj), ns, &sd);
        if (!error)
                kobj->sd = sd;
        return error;
@@ -768,15 +728,14 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry,
        struct sysfs_dirent *parent_sd = parent->d_fsdata;
        struct sysfs_dirent *sd;
        struct inode *inode;
-       enum kobj_ns_type type;
-       const void *ns;
+       const void *ns = NULL;
 
        mutex_lock(&sysfs_mutex);
 
-       type = sysfs_ns_type(parent_sd);
-       ns = sysfs_info(dir->i_sb)->ns[type];
+       if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS)
+               ns = sysfs_info(dir->i_sb)->ns;
 
-       sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name);
+       sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns);
 
        /* no such entry */
        if (!sd) {
@@ -807,41 +766,128 @@ const struct inode_operations sysfs_dir_inode_operations = {
        .setxattr       = sysfs_setxattr,
 };
 
-static void remove_dir(struct sysfs_dirent *sd)
+static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos)
 {
-       struct sysfs_addrm_cxt acxt;
+       struct sysfs_dirent *last;
 
-       sysfs_addrm_start(&acxt, sd->s_parent);
-       sysfs_remove_one(&acxt, sd);
-       sysfs_addrm_finish(&acxt);
+       while (true) {
+               struct rb_node *rbn;
+
+               last = pos;
+
+               if (sysfs_type(pos) != SYSFS_DIR)
+                       break;
+
+               rbn = rb_first(&pos->s_dir.children);
+               if (!rbn)
+                       break;
+
+               pos = to_sysfs_dirent(rbn);
+       }
+
+       return last;
 }
 
-void sysfs_remove_subdir(struct sysfs_dirent *sd)
+/**
+ * sysfs_next_descendant_post - find the next descendant for post-order walk
+ * @pos: the current position (%NULL to initiate traversal)
+ * @root: sysfs_dirent whose descendants to walk
+ *
+ * Find the next descendant to visit for post-order traversal of @root's
+ * descendants.  @root is included in the iteration and the last node to be
+ * visited.
+ */
+static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos,
+                                                      struct sysfs_dirent *root)
 {
-       remove_dir(sd);
+       struct rb_node *rbn;
+
+       lockdep_assert_held(&sysfs_mutex);
+
+       /* if first iteration, visit leftmost descendant which may be root */
+       if (!pos)
+               return sysfs_leftmost_descendant(root);
+
+       /* if we visited @root, we're done */
+       if (pos == root)
+               return NULL;
+
+       /* if there's an unvisited sibling, visit its leftmost descendant */
+       rbn = rb_next(&pos->s_rb);
+       if (rbn)
+               return sysfs_leftmost_descendant(to_sysfs_dirent(rbn));
+
+       /* no sibling left, visit parent */
+       return pos->s_parent;
 }
 
+static void __sysfs_remove(struct sysfs_addrm_cxt *acxt,
+                          struct sysfs_dirent *sd)
+{
+       struct sysfs_dirent *pos, *next;
+
+       if (!sd)
+               return;
+
+       pr_debug("sysfs %s: removing\n", sd->s_name);
+
+       next = NULL;
+       do {
+               pos = next;
+               next = sysfs_next_descendant_post(pos, sd);
+               if (pos)
+                       sysfs_remove_one(acxt, pos);
+       } while (next);
+}
 
-static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
+/**
+ * sysfs_remove - remove a sysfs_dirent recursively
+ * @sd: the sysfs_dirent to remove
+ *
+ * Remove @sd along with all its subdirectories and files.
+ */
+void sysfs_remove(struct sysfs_dirent *sd)
 {
        struct sysfs_addrm_cxt acxt;
-       struct rb_node *pos;
 
-       if (!dir_sd)
-               return;
+       sysfs_addrm_start(&acxt);
+       __sysfs_remove(&acxt, sd);
+       sysfs_addrm_finish(&acxt);
+}
 
-       pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
-       sysfs_addrm_start(&acxt, dir_sd);
-       pos = rb_first(&dir_sd->s_dir.children);
-       while (pos) {
-               struct sysfs_dirent *sd = to_sysfs_dirent(pos);
-               pos = rb_next(pos);
-               if (sysfs_type(sd) != SYSFS_DIR)
-                       sysfs_remove_one(&acxt, sd);
+/**
+ * sysfs_hash_and_remove - find a sysfs_dirent by name and remove it
+ * @dir_sd: parent of the target
+ * @name: name of the sysfs_dirent to remove
+ * @ns: namespace tag of the sysfs_dirent to remove
+ *
+ * Look for the sysfs_dirent with @name and @ns under @dir_sd and remove
+ * it.  Returns 0 on success, -ENOENT if such entry doesn't exist.
+ */
+int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name,
+                         const void *ns)
+{
+       struct sysfs_addrm_cxt acxt;
+       struct sysfs_dirent *sd;
+
+       if (!dir_sd) {
+               WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n",
+                       name);
+               return -ENOENT;
        }
+
+       sysfs_addrm_start(&acxt);
+
+       sd = sysfs_find_dirent(dir_sd, name, ns);
+       if (sd)
+               __sysfs_remove(&acxt, sd);
+
        sysfs_addrm_finish(&acxt);
 
-       remove_dir(dir_sd);
+       if (sd)
+               return 0;
+       else
+               return -ENOENT;
 }
 
 /**
@@ -852,21 +898,34 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
  *     the directory before we remove the directory, and we've inlined
  *     what used to be sysfs_rmdir() below, instead of calling separately.
  */
-
 void sysfs_remove_dir(struct kobject *kobj)
 {
        struct sysfs_dirent *sd = kobj->sd;
 
-       spin_lock(&sysfs_assoc_lock);
+       /*
+        * In general, kboject owner is responsible for ensuring removal
+        * doesn't race with other operations and sysfs doesn't provide any
+        * protection; however, when @kobj is used as a symlink target, the
+        * symlinking entity usually doesn't own @kobj and thus has no
+        * control over removal.  @kobj->sd may be removed anytime and
+        * symlink code may end up dereferencing an already freed sd.
+        *
+        * sysfs_symlink_target_lock synchronizes @kobj->sd disassociation
+        * against symlink operations so that symlink code can safely
+        * dereference @kobj->sd.
+        */
+       spin_lock(&sysfs_symlink_target_lock);
        kobj->sd = NULL;
-       spin_unlock(&sysfs_assoc_lock);
+       spin_unlock(&sysfs_symlink_target_lock);
 
-       __sysfs_remove_dir(sd);
+       if (sd) {
+               WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR);
+               sysfs_remove(sd);
+       }
 }
 
-int sysfs_rename(struct sysfs_dirent *sd,
-       struct sysfs_dirent *new_parent_sd, const void *new_ns,
-       const char *new_name)
+int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd,
+                const char *new_name, const void *new_ns)
 {
        int error;
 
@@ -878,7 +937,7 @@ int sysfs_rename(struct sysfs_dirent *sd,
                goto out;       /* nothing to rename */
 
        error = -EEXIST;
-       if (sysfs_find_dirent(new_parent_sd, new_ns, new_name))
+       if (sysfs_find_dirent(new_parent_sd, new_name, new_ns))
                goto out;
 
        /* rename sysfs_dirent */
@@ -899,7 +958,7 @@ int sysfs_rename(struct sysfs_dirent *sd,
        sysfs_get(new_parent_sd);
        sysfs_put(sd->s_parent);
        sd->s_ns = new_ns;
-       sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
+       sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns);
        sd->s_parent = new_parent_sd;
        sysfs_link_sibling(sd);
 
@@ -909,30 +968,25 @@ int sysfs_rename(struct sysfs_dirent *sd,
        return error;
 }
 
-int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
+int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
+                       const void *new_ns)
 {
        struct sysfs_dirent *parent_sd = kobj->sd->s_parent;
-       const void *new_ns = NULL;
-
-       if (sysfs_ns_type(parent_sd))
-               new_ns = kobj->ktype->namespace(kobj);
 
-       return sysfs_rename(kobj->sd, parent_sd, new_ns, new_name);
+       return sysfs_rename(kobj->sd, parent_sd, new_name, new_ns);
 }
 
-int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
+int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
+                     const void *new_ns)
 {
        struct sysfs_dirent *sd = kobj->sd;
        struct sysfs_dirent *new_parent_sd;
-       const void *new_ns = NULL;
 
        BUG_ON(!sd->s_parent);
-       if (sysfs_ns_type(sd->s_parent))
-               new_ns = kobj->ktype->namespace(kobj);
        new_parent_sd = new_parent_kobj && new_parent_kobj->sd ?
                new_parent_kobj->sd : &sysfs_root;
 
-       return sysfs_rename(sd, new_parent_sd, new_ns, sd->s_name);
+       return sysfs_rename(sd, new_parent_sd, sd->s_name, new_ns);
 }
 
 /* Relationship between s_mode and the DT_xxx types */
@@ -1002,15 +1056,15 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx)
        struct dentry *dentry = file->f_path.dentry;
        struct sysfs_dirent *parent_sd = dentry->d_fsdata;
        struct sysfs_dirent *pos = file->private_data;
-       enum kobj_ns_type type;
-       const void *ns;
-
-       type = sysfs_ns_type(parent_sd);
-       ns = sysfs_info(dentry->d_sb)->ns[type];
+       const void *ns = NULL;
 
        if (!dir_emit_dots(file, ctx))
                return 0;
        mutex_lock(&sysfs_mutex);
+
+       if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS)
+               ns = sysfs_info(dentry->d_sb)->ns;
+
        for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos);
             pos;
             pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) {
index 15ef5eb13663f6828b15e619b5e801b4700a3a82..79b5da2acbe184353475f53ccb03793404bd3563 100644 (file)
 #include <linux/mutex.h>
 #include <linux/limits.h>
 #include <linux/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
 
 #include "sysfs.h"
 
 /*
- * There's one sysfs_buffer for each open file and one
- * sysfs_open_dirent for each sysfs_dirent with one or more open
- * files.
+ * There's one sysfs_open_file for each open file and one sysfs_open_dirent
+ * for each sysfs_dirent with one or more open files.
  *
- * filp->private_data points to sysfs_buffer and
- * sysfs_dirent->s_attr.open points to sysfs_open_dirent.  s_attr.open
- * is protected by sysfs_open_dirent_lock.
+ * sysfs_dirent->s_attr.open points to sysfs_open_dirent.  s_attr.open is
+ * protected by sysfs_open_dirent_lock.
+ *
+ * filp->private_data points to seq_file whose ->private points to
+ * sysfs_open_file.  sysfs_open_files are chained at
+ * sysfs_open_dirent->files, which is protected by sysfs_open_file_mutex.
  */
 static DEFINE_SPINLOCK(sysfs_open_dirent_lock);
+static DEFINE_MUTEX(sysfs_open_file_mutex);
 
 struct sysfs_open_dirent {
        atomic_t                refcnt;
        atomic_t                event;
        wait_queue_head_t       poll;
-       struct list_head        buffers; /* goes through sysfs_buffer.list */
+       struct list_head        files; /* goes through sysfs_open_file.list */
 };
 
-struct sysfs_buffer {
-       size_t                  count;
-       loff_t                  pos;
-       char                    *page;
-       const struct sysfs_ops  *ops;
+struct sysfs_open_file {
+       struct sysfs_dirent     *sd;
+       struct file             *file;
        struct mutex            mutex;
-       int                     needs_read_fill;
        int                     event;
        struct list_head        list;
+
+       bool                    mmapped;
+       const struct vm_operations_struct *vm_ops;
 };
 
-/**
- *     fill_read_buffer - allocate and fill buffer from object.
- *     @dentry:        dentry pointer.
- *     @buffer:        data buffer for file.
- *
- *     Allocate @buffer->page, if it hasn't been already, then call the
- *     kobject's show() method to fill the buffer with this attribute's
- *     data.
- *     This is called only once, on the file's first read unless an error
- *     is returned.
+static bool sysfs_is_bin(struct sysfs_dirent *sd)
+{
+       return sysfs_type(sd) == SYSFS_KOBJ_BIN_ATTR;
+}
+
+static struct sysfs_open_file *sysfs_of(struct file *file)
+{
+       return ((struct seq_file *)file->private_data)->private;
+}
+
+/*
+ * Determine ktype->sysfs_ops for the given sysfs_dirent.  This function
+ * must be called while holding an active reference.
  */
-static int fill_read_buffer(struct dentry *dentry, struct sysfs_buffer *buffer)
+static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd)
 {
-       struct sysfs_dirent *attr_sd = dentry->d_fsdata;
-       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
-       const struct sysfs_ops *ops = buffer->ops;
-       int ret = 0;
+       struct kobject *kobj = sd->s_parent->s_dir.kobj;
+
+       if (!sysfs_ignore_lockdep(sd))
+               lockdep_assert_held(sd);
+       return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
+}
+
+/*
+ * Reads on sysfs are handled through seq_file, which takes care of hairy
+ * details like buffering and seeking.  The following function pipes
+ * sysfs_ops->show() result through seq_file.
+ */
+static int sysfs_seq_show(struct seq_file *sf, void *v)
+{
+       struct sysfs_open_file *of = sf->private;
+       struct kobject *kobj = of->sd->s_parent->s_dir.kobj;
+       const struct sysfs_ops *ops;
+       char *buf;
        ssize_t count;
 
-       if (!buffer->page)
-               buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
-       if (!buffer->page)
-               return -ENOMEM;
+       /* acquire buffer and ensure that it's >= PAGE_SIZE */
+       count = seq_get_buf(sf, &buf);
+       if (count < PAGE_SIZE) {
+               seq_commit(sf, -1);
+               return 0;
+       }
 
-       /* need attr_sd for attr and ops, its parent for kobj */
-       if (!sysfs_get_active(attr_sd))
+       /*
+        * Need @of->sd for attr and ops, its parent for kobj.  @of->mutex
+        * nests outside active ref and is just to ensure that the ops
+        * aren't called concurrently for the same open file.
+        */
+       mutex_lock(&of->mutex);
+       if (!sysfs_get_active(of->sd)) {
+               mutex_unlock(&of->mutex);
                return -ENODEV;
+       }
 
-       buffer->event = atomic_read(&attr_sd->s_attr.open->event);
-       count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);
+       of->event = atomic_read(&of->sd->s_attr.open->event);
 
-       sysfs_put_active(attr_sd);
+       /*
+        * Lookup @ops and invoke show().  Control may reach here via seq
+        * file lseek even if @ops->show() isn't implemented.
+        */
+       ops = sysfs_file_ops(of->sd);
+       if (ops->show)
+               count = ops->show(kobj, of->sd->s_attr.attr, buf);
+       else
+               count = 0;
+
+       sysfs_put_active(of->sd);
+       mutex_unlock(&of->mutex);
+
+       if (count < 0)
+               return count;
 
        /*
         * The code works fine with PAGE_SIZE return but it's likely to
@@ -96,155 +140,389 @@ static int fill_read_buffer(struct dentry *dentry, struct sysfs_buffer *buffer)
                /* Try to struggle along */
                count = PAGE_SIZE - 1;
        }
-       if (count >= 0) {
-               buffer->needs_read_fill = 0;
-               buffer->count = count;
-       } else {
-               ret = count;
-       }
-       return ret;
+       seq_commit(sf, count);
+       return 0;
 }
 
-/**
- *     sysfs_read_file - read an attribute.
- *     @file:  file pointer.
- *     @buf:   buffer to fill.
- *     @count: number of bytes to read.
- *     @ppos:  starting offset in file.
- *
- *     Userspace wants to read an attribute file. The attribute descriptor
- *     is in the file's ->d_fsdata. The target object is in the directory's
- *     ->d_fsdata.
- *
- *     We call fill_read_buffer() to allocate and fill the buffer from the
- *     object's show() method exactly once (if the read is happening from
- *     the beginning of the file). That should fill the entire buffer with
- *     all the data the object has to offer for that attribute.
- *     We then call flush_read_buffer() to copy the buffer to userspace
- *     in the increments specified.
+/*
+ * Read method for bin files.  As reading a bin file can have side-effects,
+ * the exact offset and bytes specified in read(2) call should be passed to
+ * the read callback making it difficult to use seq_file.  Implement
+ * simplistic custom buffering for bin files.
  */
-
-static ssize_t
-sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+static ssize_t sysfs_bin_read(struct file *file, char __user *userbuf,
+                             size_t bytes, loff_t *off)
 {
-       struct sysfs_buffer *buffer = file->private_data;
-       ssize_t retval = 0;
+       struct sysfs_open_file *of = sysfs_of(file);
+       struct bin_attribute *battr = of->sd->s_attr.bin_attr;
+       struct kobject *kobj = of->sd->s_parent->s_dir.kobj;
+       loff_t size = file_inode(file)->i_size;
+       int count = min_t(size_t, bytes, PAGE_SIZE);
+       loff_t offs = *off;
+       char *buf;
+
+       if (!bytes)
+               return 0;
 
-       mutex_lock(&buffer->mutex);
-       if (buffer->needs_read_fill || *ppos == 0) {
-               retval = fill_read_buffer(file->f_path.dentry, buffer);
-               if (retval)
-                       goto out;
+       if (size) {
+               if (offs > size)
+                       return 0;
+               if (offs + count > size)
+                       count = size - offs;
        }
-       pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",
-                __func__, count, *ppos, buffer->page);
-       retval = simple_read_from_buffer(buf, count, ppos, buffer->page,
-                                        buffer->count);
-out:
-       mutex_unlock(&buffer->mutex);
-       return retval;
-}
 
-/**
- *     fill_write_buffer - copy buffer from userspace.
- *     @buffer:        data buffer for file.
- *     @buf:           data from user.
- *     @count:         number of bytes in @userbuf.
- *
- *     Allocate @buffer->page if it hasn't been already, then
- *     copy the user-supplied buffer into it.
- */
-static int fill_write_buffer(struct sysfs_buffer *buffer,
-                            const char __user *buf, size_t count)
-{
-       int error;
-
-       if (!buffer->page)
-               buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
-       if (!buffer->page)
+       buf = kmalloc(count, GFP_KERNEL);
+       if (!buf)
                return -ENOMEM;
 
-       if (count >= PAGE_SIZE)
-               count = PAGE_SIZE - 1;
-       error = copy_from_user(buffer->page, buf, count);
-       buffer->needs_read_fill = 1;
-       /* if buf is assumed to contain a string, terminate it by \0,
-          so e.g. sscanf() can scan the string easily */
-       buffer->page[count] = 0;
-       return error ? -EFAULT : count;
-}
+       /* need of->sd for battr, its parent for kobj */
+       mutex_lock(&of->mutex);
+       if (!sysfs_get_active(of->sd)) {
+               count = -ENODEV;
+               mutex_unlock(&of->mutex);
+               goto out_free;
+       }
+
+       if (battr->read)
+               count = battr->read(file, kobj, battr, buf, offs, count);
+       else
+               count = -EIO;
 
+       sysfs_put_active(of->sd);
+       mutex_unlock(&of->mutex);
+
+       if (count < 0)
+               goto out_free;
+
+       if (copy_to_user(userbuf, buf, count)) {
+               count = -EFAULT;
+               goto out_free;
+       }
+
+       pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
+
+       *off = offs + count;
+
+ out_free:
+       kfree(buf);
+       return count;
+}
 
 /**
- *     flush_write_buffer - push buffer to kobject.
- *     @dentry:        dentry to the attribute
- *     @buffer:        data buffer for file.
- *     @count:         number of bytes
+ * flush_write_buffer - push buffer to kobject
+ * @of: open file
+ * @buf: data buffer for file
+ * @off: file offset to write to
+ * @count: number of bytes
  *
- *     Get the correct pointers for the kobject and the attribute we're
- *     dealing with, then call the store() method for the attribute,
- *     passing the buffer that we acquired in fill_write_buffer().
+ * Get the correct pointers for the kobject and the attribute we're dealing
+ * with, then call the store() method for it with @buf.
  */
-static int flush_write_buffer(struct dentry *dentry,
-                             struct sysfs_buffer *buffer, size_t count)
+static int flush_write_buffer(struct sysfs_open_file *of, char *buf, loff_t off,
+                             size_t count)
 {
-       struct sysfs_dirent *attr_sd = dentry->d_fsdata;
-       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
-       const struct sysfs_ops *ops = buffer->ops;
-       int rc;
+       struct kobject *kobj = of->sd->s_parent->s_dir.kobj;
+       int rc = 0;
 
-       /* need attr_sd for attr and ops, its parent for kobj */
-       if (!sysfs_get_active(attr_sd))
+       /*
+        * Need @of->sd for attr and ops, its parent for kobj.  @of->mutex
+        * nests outside active ref and is just to ensure that the ops
+        * aren't called concurrently for the same open file.
+        */
+       mutex_lock(&of->mutex);
+       if (!sysfs_get_active(of->sd)) {
+               mutex_unlock(&of->mutex);
                return -ENODEV;
+       }
 
-       rc = ops->store(kobj, attr_sd->s_attr.attr, buffer->page, count);
+       if (sysfs_is_bin(of->sd)) {
+               struct bin_attribute *battr = of->sd->s_attr.bin_attr;
 
-       sysfs_put_active(attr_sd);
+               rc = -EIO;
+               if (battr->write)
+                       rc = battr->write(of->file, kobj, battr, buf, off,
+                                         count);
+       } else {
+               const struct sysfs_ops *ops = sysfs_file_ops(of->sd);
+
+               rc = ops->store(kobj, of->sd->s_attr.attr, buf, count);
+       }
+
+       sysfs_put_active(of->sd);
+       mutex_unlock(&of->mutex);
 
        return rc;
 }
 
-
 /**
- *     sysfs_write_file - write an attribute.
- *     @file:  file pointer
- *     @buf:   data to write
- *     @count: number of bytes
- *     @ppos:  starting offset
+ * sysfs_write_file - write an attribute
+ * @file: file pointer
+ * @user_buf: data to write
+ * @count: number of bytes
+ * @ppos: starting offset
+ *
+ * Copy data in from userland and pass it to the matching
+ * sysfs_ops->store() by invoking flush_write_buffer().
  *
- *     Similar to sysfs_read_file(), though working in the opposite direction.
- *     We allocate and fill the data from the user in fill_write_buffer(),
- *     then push it to the kobject in flush_write_buffer().
- *     There is no easy way for us to know if userspace is only doing a partial
- *     write, so we don't support them. We expect the entire buffer to come
- *     on the first write.
- *     Hint: if you're writing a value, first read the file, modify only the
- *     the value you're changing, then write entire buffer back.
+ * There is no easy way for us to know if userspace is only doing a partial
+ * write, so we don't support them. We expect the entire buffer to come on
+ * the first write.  Hint: if you're writing a value, first read the file,
+ * modify only the the value you're changing, then write entire buffer
+ * back.
  */
-static ssize_t sysfs_write_file(struct file *file, const char __user *buf,
+static ssize_t sysfs_write_file(struct file *file, const char __user *user_buf,
                                size_t count, loff_t *ppos)
 {
-       struct sysfs_buffer *buffer = file->private_data;
-       ssize_t len;
+       struct sysfs_open_file *of = sysfs_of(file);
+       ssize_t len = min_t(size_t, count, PAGE_SIZE);
+       loff_t size = file_inode(file)->i_size;
+       char *buf;
+
+       if (sysfs_is_bin(of->sd) && size) {
+               if (size <= *ppos)
+                       return 0;
+               len = min_t(ssize_t, len, size - *ppos);
+       }
 
-       mutex_lock(&buffer->mutex);
-       len = fill_write_buffer(buffer, buf, count);
-       if (len > 0)
-               len = flush_write_buffer(file->f_path.dentry, buffer, len);
+       if (!len)
+               return 0;
+
+       buf = kmalloc(len + 1, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (copy_from_user(buf, user_buf, len)) {
+               len = -EFAULT;
+               goto out_free;
+       }
+       buf[len] = '\0';        /* guarantee string termination */
+
+       len = flush_write_buffer(of, buf, *ppos, len);
        if (len > 0)
                *ppos += len;
-       mutex_unlock(&buffer->mutex);
+out_free:
+       kfree(buf);
        return len;
 }
 
+static void sysfs_bin_vma_open(struct vm_area_struct *vma)
+{
+       struct file *file = vma->vm_file;
+       struct sysfs_open_file *of = sysfs_of(file);
+
+       if (!of->vm_ops)
+               return;
+
+       if (!sysfs_get_active(of->sd))
+               return;
+
+       if (of->vm_ops->open)
+               of->vm_ops->open(vma);
+
+       sysfs_put_active(of->sd);
+}
+
+static int sysfs_bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+       struct file *file = vma->vm_file;
+       struct sysfs_open_file *of = sysfs_of(file);
+       int ret;
+
+       if (!of->vm_ops)
+               return VM_FAULT_SIGBUS;
+
+       if (!sysfs_get_active(of->sd))
+               return VM_FAULT_SIGBUS;
+
+       ret = VM_FAULT_SIGBUS;
+       if (of->vm_ops->fault)
+               ret = of->vm_ops->fault(vma, vmf);
+
+       sysfs_put_active(of->sd);
+       return ret;
+}
+
+static int sysfs_bin_page_mkwrite(struct vm_area_struct *vma,
+                                 struct vm_fault *vmf)
+{
+       struct file *file = vma->vm_file;
+       struct sysfs_open_file *of = sysfs_of(file);
+       int ret;
+
+       if (!of->vm_ops)
+               return VM_FAULT_SIGBUS;
+
+       if (!sysfs_get_active(of->sd))
+               return VM_FAULT_SIGBUS;
+
+       ret = 0;
+       if (of->vm_ops->page_mkwrite)
+               ret = of->vm_ops->page_mkwrite(vma, vmf);
+       else
+               file_update_time(file);
+
+       sysfs_put_active(of->sd);
+       return ret;
+}
+
+static int sysfs_bin_access(struct vm_area_struct *vma, unsigned long addr,
+                           void *buf, int len, int write)
+{
+       struct file *file = vma->vm_file;
+       struct sysfs_open_file *of = sysfs_of(file);
+       int ret;
+
+       if (!of->vm_ops)
+               return -EINVAL;
+
+       if (!sysfs_get_active(of->sd))
+               return -EINVAL;
+
+       ret = -EINVAL;
+       if (of->vm_ops->access)
+               ret = of->vm_ops->access(vma, addr, buf, len, write);
+
+       sysfs_put_active(of->sd);
+       return ret;
+}
+
+#ifdef CONFIG_NUMA
+static int sysfs_bin_set_policy(struct vm_area_struct *vma,
+                               struct mempolicy *new)
+{
+       struct file *file = vma->vm_file;
+       struct sysfs_open_file *of = sysfs_of(file);
+       int ret;
+
+       if (!of->vm_ops)
+               return 0;
+
+       if (!sysfs_get_active(of->sd))
+               return -EINVAL;
+
+       ret = 0;
+       if (of->vm_ops->set_policy)
+               ret = of->vm_ops->set_policy(vma, new);
+
+       sysfs_put_active(of->sd);
+       return ret;
+}
+
+static struct mempolicy *sysfs_bin_get_policy(struct vm_area_struct *vma,
+                                             unsigned long addr)
+{
+       struct file *file = vma->vm_file;
+       struct sysfs_open_file *of = sysfs_of(file);
+       struct mempolicy *pol;
+
+       if (!of->vm_ops)
+               return vma->vm_policy;
+
+       if (!sysfs_get_active(of->sd))
+               return vma->vm_policy;
+
+       pol = vma->vm_policy;
+       if (of->vm_ops->get_policy)
+               pol = of->vm_ops->get_policy(vma, addr);
+
+       sysfs_put_active(of->sd);
+       return pol;
+}
+
+static int sysfs_bin_migrate(struct vm_area_struct *vma, const nodemask_t *from,
+                            const nodemask_t *to, unsigned long flags)
+{
+       struct file *file = vma->vm_file;
+       struct sysfs_open_file *of = sysfs_of(file);
+       int ret;
+
+       if (!of->vm_ops)
+               return 0;
+
+       if (!sysfs_get_active(of->sd))
+               return 0;
+
+       ret = 0;
+       if (of->vm_ops->migrate)
+               ret = of->vm_ops->migrate(vma, from, to, flags);
+
+       sysfs_put_active(of->sd);
+       return ret;
+}
+#endif
+
+static const struct vm_operations_struct sysfs_bin_vm_ops = {
+       .open           = sysfs_bin_vma_open,
+       .fault          = sysfs_bin_fault,
+       .page_mkwrite   = sysfs_bin_page_mkwrite,
+       .access         = sysfs_bin_access,
+#ifdef CONFIG_NUMA
+       .set_policy     = sysfs_bin_set_policy,
+       .get_policy     = sysfs_bin_get_policy,
+       .migrate        = sysfs_bin_migrate,
+#endif
+};
+
+static int sysfs_bin_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct sysfs_open_file *of = sysfs_of(file);
+       struct bin_attribute *battr = of->sd->s_attr.bin_attr;
+       struct kobject *kobj = of->sd->s_parent->s_dir.kobj;
+       int rc;
+
+       mutex_lock(&of->mutex);
+
+       /* need of->sd for battr, its parent for kobj */
+       rc = -ENODEV;
+       if (!sysfs_get_active(of->sd))
+               goto out_unlock;
+
+       if (!battr->mmap)
+               goto out_put;
+
+       rc = battr->mmap(file, kobj, battr, vma);
+       if (rc)
+               goto out_put;
+
+       /*
+        * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup()
+        * to satisfy versions of X which crash if the mmap fails: that
+        * substitutes a new vm_file, and we don't then want bin_vm_ops.
+        */
+       if (vma->vm_file != file)
+               goto out_put;
+
+       rc = -EINVAL;
+       if (of->mmapped && of->vm_ops != vma->vm_ops)
+               goto out_put;
+
+       /*
+        * It is not possible to successfully wrap close.
+        * So error if someone is trying to use close.
+        */
+       rc = -EINVAL;
+       if (vma->vm_ops && vma->vm_ops->close)
+               goto out_put;
+
+       rc = 0;
+       of->mmapped = 1;
+       of->vm_ops = vma->vm_ops;
+       vma->vm_ops = &sysfs_bin_vm_ops;
+out_put:
+       sysfs_put_active(of->sd);
+out_unlock:
+       mutex_unlock(&of->mutex);
+
+       return rc;
+}
+
 /**
  *     sysfs_get_open_dirent - get or create sysfs_open_dirent
  *     @sd: target sysfs_dirent
- *     @buffer: sysfs_buffer for this instance of open
+ *     @of: sysfs_open_file for this instance of open
  *
  *     If @sd->s_attr.open exists, increment its reference count;
- *     otherwise, create one.  @buffer is chained to the buffers
- *     list.
+ *     otherwise, create one.  @of is chained to the files list.
  *
  *     LOCKING:
  *     Kernel thread context (may sleep).
@@ -253,11 +531,12 @@ static ssize_t sysfs_write_file(struct file *file, const char __user *buf,
  *     0 on success, -errno on failure.
  */
 static int sysfs_get_open_dirent(struct sysfs_dirent *sd,
-                                struct sysfs_buffer *buffer)
+                                struct sysfs_open_file *of)
 {
        struct sysfs_open_dirent *od, *new_od = NULL;
 
  retry:
+       mutex_lock(&sysfs_open_file_mutex);
        spin_lock_irq(&sysfs_open_dirent_lock);
 
        if (!sd->s_attr.open && new_od) {
@@ -268,10 +547,11 @@ static int sysfs_get_open_dirent(struct sysfs_dirent *sd,
        od = sd->s_attr.open;
        if (od) {
                atomic_inc(&od->refcnt);
-               list_add_tail(&buffer->list, &od->buffers);
+               list_add_tail(&of->list, &od->files);
        }
 
        spin_unlock_irq(&sysfs_open_dirent_lock);
+       mutex_unlock(&sysfs_open_file_mutex);
 
        if (od) {
                kfree(new_od);
@@ -286,36 +566,40 @@ static int sysfs_get_open_dirent(struct sysfs_dirent *sd,
        atomic_set(&new_od->refcnt, 0);
        atomic_set(&new_od->event, 1);
        init_waitqueue_head(&new_od->poll);
-       INIT_LIST_HEAD(&new_od->buffers);
+       INIT_LIST_HEAD(&new_od->files);
        goto retry;
 }
 
 /**
  *     sysfs_put_open_dirent - put sysfs_open_dirent
  *     @sd: target sysfs_dirent
- *     @buffer: associated sysfs_buffer
+ *     @of: associated sysfs_open_file
  *
- *     Put @sd->s_attr.open and unlink @buffer from the buffers list.
- *     If reference count reaches zero, disassociate and free it.
+ *     Put @sd->s_attr.open and unlink @of from the files list.  If
+ *     reference count reaches zero, disassociate and free it.
  *
  *     LOCKING:
  *     None.
  */
 static void sysfs_put_open_dirent(struct sysfs_dirent *sd,
-                                 struct sysfs_buffer *buffer)
+                                 struct sysfs_open_file *of)
 {
        struct sysfs_open_dirent *od = sd->s_attr.open;
        unsigned long flags;
 
+       mutex_lock(&sysfs_open_file_mutex);
        spin_lock_irqsave(&sysfs_open_dirent_lock, flags);
 
-       list_del(&buffer->list);
+       if (of)
+               list_del(&of->list);
+
        if (atomic_dec_and_test(&od->refcnt))
                sd->s_attr.open = NULL;
        else
                od = NULL;
 
        spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags);
+       mutex_unlock(&sysfs_open_file_mutex);
 
        kfree(od);
 }
@@ -324,67 +608,81 @@ static int sysfs_open_file(struct inode *inode, struct file *file)
 {
        struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
        struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
-       struct sysfs_buffer *buffer;
-       const struct sysfs_ops *ops;
+       struct sysfs_open_file *of;
+       bool has_read, has_write;
        int error = -EACCES;
 
        /* need attr_sd for attr and ops, its parent for kobj */
        if (!sysfs_get_active(attr_sd))
                return -ENODEV;
 
-       /* every kobject with an attribute needs a ktype assigned */
-       if (kobj->ktype && kobj->ktype->sysfs_ops)
-               ops = kobj->ktype->sysfs_ops;
-       else {
-               WARN(1, KERN_ERR
-                    "missing sysfs attribute operations for kobject: %s\n",
-                    kobject_name(kobj));
-               goto err_out;
-       }
+       if (sysfs_is_bin(attr_sd)) {
+               struct bin_attribute *battr = attr_sd->s_attr.bin_attr;
 
-       /* File needs write support.
-        * The inode's perms must say it's ok,
-        * and we must have a store method.
-        */
-       if (file->f_mode & FMODE_WRITE) {
-               if (!(inode->i_mode & S_IWUGO) || !ops->store)
-                       goto err_out;
-       }
+               has_read = battr->read || battr->mmap;
+               has_write = battr->write || battr->mmap;
+       } else {
+               const struct sysfs_ops *ops = sysfs_file_ops(attr_sd);
 
-       /* File needs read support.
-        * The inode's perms must say it's ok, and we there
-        * must be a show method for it.
-        */
-       if (file->f_mode & FMODE_READ) {
-               if (!(inode->i_mode & S_IRUGO) || !ops->show)
+               /* every kobject with an attribute needs a ktype assigned */
+               if (WARN(!ops, KERN_ERR
+                        "missing sysfs attribute operations for kobject: %s\n",
+                        kobject_name(kobj)))
                        goto err_out;
+
+               has_read = ops->show;
+               has_write = ops->store;
        }
 
-       /* No error? Great, allocate a buffer for the file, and store it
-        * it in file->private_data for easy access.
-        */
+       /* check perms and supported operations */
+       if ((file->f_mode & FMODE_WRITE) &&
+           (!(inode->i_mode & S_IWUGO) || !has_write))
+               goto err_out;
+
+       if ((file->f_mode & FMODE_READ) &&
+           (!(inode->i_mode & S_IRUGO) || !has_read))
+               goto err_out;
+
+       /* allocate a sysfs_open_file for the file */
        error = -ENOMEM;
-       buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
-       if (!buffer)
+       of = kzalloc(sizeof(struct sysfs_open_file), GFP_KERNEL);
+       if (!of)
                goto err_out;
 
-       mutex_init(&buffer->mutex);
-       buffer->needs_read_fill = 1;
-       buffer->ops = ops;
-       file->private_data = buffer;
+       mutex_init(&of->mutex);
+       of->sd = attr_sd;
+       of->file = file;
 
-       /* make sure we have open dirent struct */
-       error = sysfs_get_open_dirent(attr_sd, buffer);
+       /*
+        * Always instantiate seq_file even if read access doesn't use
+        * seq_file or is not requested.  This unifies private data access
+        * and readable regular files are the vast majority anyway.
+        */
+       if (sysfs_is_bin(attr_sd))
+               error = single_open(file, NULL, of);
+       else
+               error = single_open(file, sysfs_seq_show, of);
        if (error)
                goto err_free;
 
+       /* seq_file clears PWRITE unconditionally, restore it if WRITE */
+       if (file->f_mode & FMODE_WRITE)
+               file->f_mode |= FMODE_PWRITE;
+
+       /* make sure we have open dirent struct */
+       error = sysfs_get_open_dirent(attr_sd, of);
+       if (error)
+               goto err_close;
+
        /* open succeeded, put active references */
        sysfs_put_active(attr_sd);
        return 0;
 
- err_free:
-       kfree(buffer);
- err_out:
+err_close:
+       single_release(inode, file);
+err_free:
+       kfree(of);
+err_out:
        sysfs_put_active(attr_sd);
        return error;
 }
@@ -392,17 +690,41 @@ static int sysfs_open_file(struct inode *inode, struct file *file)
 static int sysfs_release(struct inode *inode, struct file *filp)
 {
        struct sysfs_dirent *sd = filp->f_path.dentry->d_fsdata;
-       struct sysfs_buffer *buffer = filp->private_data;
+       struct sysfs_open_file *of = sysfs_of(filp);
 
-       sysfs_put_open_dirent(sd, buffer);
-
-       if (buffer->page)
-               free_page((unsigned long)buffer->page);
-       kfree(buffer);
+       sysfs_put_open_dirent(sd, of);
+       single_release(inode, filp);
+       kfree(of);
 
        return 0;
 }
 
+void sysfs_unmap_bin_file(struct sysfs_dirent *sd)
+{
+       struct sysfs_open_dirent *od;
+       struct sysfs_open_file *of;
+
+       if (!sysfs_is_bin(sd))
+               return;
+
+       spin_lock_irq(&sysfs_open_dirent_lock);
+       od = sd->s_attr.open;
+       if (od)
+               atomic_inc(&od->refcnt);
+       spin_unlock_irq(&sysfs_open_dirent_lock);
+       if (!od)
+               return;
+
+       mutex_lock(&sysfs_open_file_mutex);
+       list_for_each_entry(of, &od->files, list) {
+               struct inode *inode = file_inode(of->file);
+               unmap_mapping_range(inode->i_mapping, 0, 0, 1);
+       }
+       mutex_unlock(&sysfs_open_file_mutex);
+
+       sysfs_put_open_dirent(sd, NULL);
+}
+
 /* Sysfs attribute files are pollable.  The idea is that you read
  * the content and then you use 'poll' or 'select' to wait for
  * the content to change.  When the content changes (assuming the
@@ -418,7 +740,7 @@ static int sysfs_release(struct inode *inode, struct file *filp)
  */
 static unsigned int sysfs_poll(struct file *filp, poll_table *wait)
 {
-       struct sysfs_buffer *buffer = filp->private_data;
+       struct sysfs_open_file *of = sysfs_of(filp);
        struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata;
        struct sysfs_open_dirent *od = attr_sd->s_attr.open;
 
@@ -430,13 +752,12 @@ static unsigned int sysfs_poll(struct file *filp, poll_table *wait)
 
        sysfs_put_active(attr_sd);
 
-       if (buffer->event != atomic_read(&od->event))
+       if (of->event != atomic_read(&od->event))
                goto trigger;
 
        return DEFAULT_POLLMASK;
 
  trigger:
-       buffer->needs_read_fill = 1;
        return DEFAULT_POLLMASK|POLLERR|POLLPRI;
 }
 
@@ -466,9 +787,9 @@ void sysfs_notify(struct kobject *k, const char *dir, const char *attr)
        mutex_lock(&sysfs_mutex);
 
        if (sd && dir)
-               sd = sysfs_find_dirent(sd, NULL, dir);
+               sd = sysfs_find_dirent(sd, dir, NULL);
        if (sd && attr)
-               sd = sysfs_find_dirent(sd, NULL, attr);
+               sd = sysfs_find_dirent(sd, attr, NULL);
        if (sd)
                sysfs_notify_dirent(sd);
 
@@ -477,7 +798,7 @@ void sysfs_notify(struct kobject *k, const char *dir, const char *attr)
 EXPORT_SYMBOL_GPL(sysfs_notify);
 
 const struct file_operations sysfs_file_operations = {
-       .read           = sysfs_read_file,
+       .read           = seq_read,
        .write          = sysfs_write_file,
        .llseek         = generic_file_llseek,
        .open           = sysfs_open_file,
@@ -485,58 +806,25 @@ const struct file_operations sysfs_file_operations = {
        .poll           = sysfs_poll,
 };
 
-static int sysfs_attr_ns(struct kobject *kobj, const struct attribute *attr,
-                        const void **pns)
-{
-       struct sysfs_dirent *dir_sd = kobj->sd;
-       const struct sysfs_ops *ops;
-       const void *ns = NULL;
-       int err;
-
-       if (!dir_sd) {
-               WARN(1, KERN_ERR "sysfs: kobject %s without dirent\n",
-                       kobject_name(kobj));
-               return -ENOENT;
-       }
-
-       err = 0;
-       if (!sysfs_ns_type(dir_sd))
-               goto out;
-
-       err = -EINVAL;
-       if (!kobj->ktype)
-               goto out;
-       ops = kobj->ktype->sysfs_ops;
-       if (!ops)
-               goto out;
-       if (!ops->namespace)
-               goto out;
-
-       err = 0;
-       ns = ops->namespace(kobj, attr);
-out:
-       if (err) {
-               WARN(1, KERN_ERR
-                    "missing sysfs namespace attribute operation for kobject: %s\n",
-                    kobject_name(kobj));
-       }
-       *pns = ns;
-       return err;
-}
+const struct file_operations sysfs_bin_operations = {
+       .read           = sysfs_bin_read,
+       .write          = sysfs_write_file,
+       .llseek         = generic_file_llseek,
+       .mmap           = sysfs_bin_mmap,
+       .open           = sysfs_open_file,
+       .release        = sysfs_release,
+       .poll           = sysfs_poll,
+};
 
-int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
-                       const struct attribute *attr, int type, umode_t amode)
+int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd,
+                          const struct attribute *attr, int type,
+                          umode_t amode, const void *ns)
 {
        umode_t mode = (amode & S_IALLUGO) | S_IFREG;
        struct sysfs_addrm_cxt acxt;
        struct sysfs_dirent *sd;
-       const void *ns;
        int rc;
 
-       rc = sysfs_attr_ns(dir_sd->s_dir.kobj, attr, &ns);
-       if (rc)
-               return rc;
-
        sd = sysfs_new_dirent(attr->name, mode, type);
        if (!sd)
                return -ENOMEM;
@@ -545,8 +833,8 @@ int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
        sd->s_attr.attr = (void *)attr;
        sysfs_dirent_init_lockdep(sd);
 
-       sysfs_addrm_start(&acxt, dir_sd);
-       rc = sysfs_add_one(&acxt, sd);
+       sysfs_addrm_start(&acxt);
+       rc = sysfs_add_one(&acxt, sd, dir_sd);
        sysfs_addrm_finish(&acxt);
 
        if (rc)
@@ -559,23 +847,25 @@ int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
 int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
                   int type)
 {
-       return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
+       return sysfs_add_file_mode_ns(dir_sd, attr, type, attr->mode, NULL);
 }
 
-
 /**
- *     sysfs_create_file - create an attribute file for an object.
- *     @kobj:  object we're creating for.
- *     @attr:  attribute descriptor.
+ * sysfs_create_file_ns - create an attribute file for an object with custom ns
+ * @kobj: object we're creating for
+ * @attr: attribute descriptor
+ * @ns: namespace the new file should belong to
  */
-int sysfs_create_file(struct kobject *kobj, const struct attribute *attr)
+int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
+                        const void *ns)
 {
        BUG_ON(!kobj || !kobj->sd || !attr);
 
-       return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
+       return sysfs_add_file_mode_ns(kobj->sd, attr, SYSFS_KOBJ_ATTR,
+                                     attr->mode, ns);
 
 }
-EXPORT_SYMBOL_GPL(sysfs_create_file);
+EXPORT_SYMBOL_GPL(sysfs_create_file_ns);
 
 int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr)
 {
@@ -604,7 +894,7 @@ int sysfs_add_file_to_group(struct kobject *kobj,
        int error;
 
        if (group)
-               dir_sd = sysfs_get_dirent(kobj->sd, NULL, group);
+               dir_sd = sysfs_get_dirent(kobj->sd, group);
        else
                dir_sd = sysfs_get(kobj->sd);
 
@@ -630,17 +920,12 @@ int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr,
 {
        struct sysfs_dirent *sd;
        struct iattr newattrs;
-       const void *ns;
        int rc;
 
-       rc = sysfs_attr_ns(kobj, attr, &ns);
-       if (rc)
-               return rc;
-
        mutex_lock(&sysfs_mutex);
 
        rc = -ENOENT;
-       sd = sysfs_find_dirent(kobj->sd, ns, attr->name);
+       sd = sysfs_find_dirent(kobj->sd, attr->name, NULL);
        if (!sd)
                goto out;
 
@@ -655,22 +940,21 @@ int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr,
 EXPORT_SYMBOL_GPL(sysfs_chmod_file);
 
 /**
- *     sysfs_remove_file - remove an object attribute.
- *     @kobj:  object we're acting for.
- *     @attr:  attribute descriptor.
+ * sysfs_remove_file_ns - remove an object attribute with a custom ns tag
+ * @kobj: object we're acting for
+ * @attr: attribute descriptor
+ * @ns: namespace tag of the file to remove
  *
- *     Hash the attribute name and kill the victim.
+ * Hash the attribute name and namespace tag and kill the victim.
  */
-void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
+void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
+                         const void *ns)
 {
-       const void *ns;
-
-       if (sysfs_attr_ns(kobj, attr, &ns))
-               return;
+       struct sysfs_dirent *dir_sd = kobj->sd;
 
-       sysfs_hash_and_remove(kobj->sd, ns, attr->name);
+       sysfs_hash_and_remove(dir_sd, attr->name, ns);
 }
-EXPORT_SYMBOL_GPL(sysfs_remove_file);
+EXPORT_SYMBOL_GPL(sysfs_remove_file_ns);
 
 void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr)
 {
@@ -692,16 +976,42 @@ void sysfs_remove_file_from_group(struct kobject *kobj,
        struct sysfs_dirent *dir_sd;
 
        if (group)
-               dir_sd = sysfs_get_dirent(kobj->sd, NULL, group);
+               dir_sd = sysfs_get_dirent(kobj->sd, group);
        else
                dir_sd = sysfs_get(kobj->sd);
        if (dir_sd) {
-               sysfs_hash_and_remove(dir_sd, NULL, attr->name);
+               sysfs_hash_and_remove(dir_sd, attr->name, NULL);
                sysfs_put(dir_sd);
        }
 }
 EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
 
+/**
+ *     sysfs_create_bin_file - create binary file for object.
+ *     @kobj:  object.
+ *     @attr:  attribute descriptor.
+ */
+int sysfs_create_bin_file(struct kobject *kobj,
+                         const struct bin_attribute *attr)
+{
+       BUG_ON(!kobj || !kobj->sd || !attr);
+
+       return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
+}
+EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
+
+/**
+ *     sysfs_remove_bin_file - remove binary file for object.
+ *     @kobj:  object.
+ *     @attr:  attribute descriptor.
+ */
+void sysfs_remove_bin_file(struct kobject *kobj,
+                          const struct bin_attribute *attr)
+{
+       sysfs_hash_and_remove(kobj->sd, attr->attr.name, NULL);
+}
+EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
+
 struct sysfs_schedule_callback_struct {
        struct list_head        workq_list;
        struct kobject          *kobj;
index 5f92cd2f61c1fe118168be8978a9d5c9846c5132..1898a10e38ce8ccef26a687882c637f349ac5b29 100644 (file)
@@ -26,7 +26,7 @@ static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
 
        if (grp->attrs)
                for (attr = grp->attrs; *attr; attr++)
-                       sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name);
+                       sysfs_hash_and_remove(dir_sd, (*attr)->name, NULL);
        if (grp->bin_attrs)
                for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
                        sysfs_remove_bin_file(kobj, *bin_attr);
@@ -49,16 +49,17 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
                         * re-adding (if required) the file.
                         */
                        if (update)
-                               sysfs_hash_and_remove(dir_sd, NULL,
-                                                     (*attr)->name);
+                               sysfs_hash_and_remove(dir_sd, (*attr)->name,
+                                                     NULL);
                        if (grp->is_visible) {
                                mode = grp->is_visible(kobj, *attr, i);
                                if (!mode)
                                        continue;
                        }
-                       error = sysfs_add_file_mode(dir_sd, *attr,
-                                                   SYSFS_KOBJ_ATTR,
-                                                   (*attr)->mode | mode);
+                       error = sysfs_add_file_mode_ns(dir_sd, *attr,
+                                                      SYSFS_KOBJ_ATTR,
+                                                      (*attr)->mode | mode,
+                                                      NULL);
                        if (unlikely(error))
                                break;
                }
@@ -110,7 +111,7 @@ static int internal_create_group(struct kobject *kobj, int update,
        error = create_files(sd, kobj, grp, update);
        if (error) {
                if (grp->name)
-                       sysfs_remove_subdir(sd);
+                       sysfs_remove(sd);
        }
        sysfs_put(sd);
        return error;
@@ -206,7 +207,7 @@ void sysfs_remove_group(struct kobject *kobj,
        struct sysfs_dirent *sd;
 
        if (grp->name) {
-               sd = sysfs_get_dirent(dir_sd, NULL, grp->name);
+               sd = sysfs_get_dirent(dir_sd, grp->name);
                if (!sd) {
                        WARN(!sd, KERN_WARNING
                             "sysfs group %p not found for kobject '%s'\n",
@@ -218,7 +219,7 @@ void sysfs_remove_group(struct kobject *kobj,
 
        remove_files(sd, kobj, grp);
        if (grp->name)
-               sysfs_remove_subdir(sd);
+               sysfs_remove(sd);
 
        sysfs_put(sd);
 }
@@ -261,7 +262,7 @@ int sysfs_merge_group(struct kobject *kobj,
        struct attribute *const *attr;
        int i;
 
-       dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name);
+       dir_sd = sysfs_get_dirent(kobj->sd, grp->name);
        if (!dir_sd)
                return -ENOENT;
 
@@ -269,7 +270,7 @@ int sysfs_merge_group(struct kobject *kobj,
                error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR);
        if (error) {
                while (--i >= 0)
-                       sysfs_hash_and_remove(dir_sd, NULL, (*--attr)->name);
+                       sysfs_hash_and_remove(dir_sd, (*--attr)->name, NULL);
        }
        sysfs_put(dir_sd);
 
@@ -288,10 +289,10 @@ void sysfs_unmerge_group(struct kobject *kobj,
        struct sysfs_dirent *dir_sd;
        struct attribute *const *attr;
 
-       dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name);
+       dir_sd = sysfs_get_dirent(kobj->sd, grp->name);
        if (dir_sd) {
                for (attr = grp->attrs; *attr; ++attr)
-                       sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name);
+                       sysfs_hash_and_remove(dir_sd, (*attr)->name, NULL);
                sysfs_put(dir_sd);
        }
 }
@@ -310,7 +311,7 @@ int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
        struct sysfs_dirent *dir_sd;
        int error = 0;
 
-       dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name);
+       dir_sd = sysfs_get_dirent(kobj->sd, group_name);
        if (!dir_sd)
                return -ENOENT;
 
@@ -332,9 +333,9 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
 {
        struct sysfs_dirent *dir_sd;
 
-       dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name);
+       dir_sd = sysfs_get_dirent(kobj->sd, group_name);
        if (dir_sd) {
-               sysfs_hash_and_remove(dir_sd, NULL, link_name);
+               sysfs_hash_and_remove(dir_sd, link_name, NULL);
                sysfs_put(dir_sd);
        }
 }
index 963f910c8034f84d414e625c092d45d2ca564123..1750f790af3b9a3714580c73705acc3cb21c613b 100644 (file)
@@ -258,9 +258,9 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
                inode->i_fop = &sysfs_file_operations;
                break;
        case SYSFS_KOBJ_BIN_ATTR:
-               bin_attr = sd->s_bin_attr.bin_attr;
+               bin_attr = sd->s_attr.bin_attr;
                inode->i_size = bin_attr->size;
-               inode->i_fop = &bin_fops;
+               inode->i_fop = &sysfs_bin_operations;
                break;
        case SYSFS_KOBJ_LINK:
                inode->i_op = &sysfs_symlink_inode_operations;
@@ -314,32 +314,6 @@ void sysfs_evict_inode(struct inode *inode)
        sysfs_put(sd);
 }
 
-int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns,
-                         const char *name)
-{
-       struct sysfs_addrm_cxt acxt;
-       struct sysfs_dirent *sd;
-
-       if (!dir_sd) {
-               WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n",
-                       name);
-               return -ENOENT;
-       }
-
-       sysfs_addrm_start(&acxt, dir_sd);
-
-       sd = sysfs_find_dirent(dir_sd, ns, name);
-       if (sd)
-               sysfs_remove_one(&acxt, sd);
-
-       sysfs_addrm_finish(&acxt);
-
-       if (sd)
-               return 0;
-       else
-               return -ENOENT;
-}
-
 int sysfs_permission(struct inode *inode, int mask)
 {
        struct sysfs_dirent *sd;
index 834ec2cdb7a37e070b7e5ed04ec684ba46cb2093..8c24bce2f4ae514770302f53ca85f6bb00da7260 100644 (file)
@@ -36,7 +36,7 @@ static const struct super_operations sysfs_ops = {
 struct sysfs_dirent sysfs_root = {
        .s_name         = "",
        .s_count        = ATOMIC_INIT(1),
-       .s_flags        = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),
+       .s_flags        = SYSFS_DIR,
        .s_mode         = S_IFDIR | S_IRUGO | S_IXUGO,
        .s_ino          = 1,
 };
@@ -77,14 +77,8 @@ static int sysfs_test_super(struct super_block *sb, void *data)
 {
        struct sysfs_super_info *sb_info = sysfs_info(sb);
        struct sysfs_super_info *info = data;
-       enum kobj_ns_type type;
-       int found = 1;
 
-       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {
-               if (sb_info->ns[type] != info->ns[type])
-                       found = 0;
-       }
-       return found;
+       return sb_info->ns == info->ns;
 }
 
 static int sysfs_set_super(struct super_block *sb, void *data)
@@ -98,9 +92,7 @@ static int sysfs_set_super(struct super_block *sb, void *data)
 
 static void free_sysfs_super_info(struct sysfs_super_info *info)
 {
-       int type;
-       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
-               kobj_ns_drop(type, info->ns[type]);
+       kobj_ns_drop(KOBJ_NS_TYPE_NET, info->ns);
        kfree(info);
 }
 
@@ -108,7 +100,6 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data)
 {
        struct sysfs_super_info *info;
-       enum kobj_ns_type type;
        struct super_block *sb;
        int error;
 
@@ -116,18 +107,15 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
                if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type))
                        return ERR_PTR(-EPERM);
 
-               for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {
-                       if (!kobj_ns_current_may_mount(type))
-                               return ERR_PTR(-EPERM);
-               }
+               if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET))
+                       return ERR_PTR(-EPERM);
        }
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                return ERR_PTR(-ENOMEM);
 
-       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
-               info->ns[type] = kobj_ns_grab_current(type);
+       info->ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET);
 
        sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info);
        if (IS_ERR(sb) || sb->s_fs_info != info)
index 2dd4507d9edd675b78a5c857d337a8abc9cf76cd..1a23681b817901a46dad3e4432c54de147248d37 100644 (file)
@@ -28,18 +28,19 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd,
        struct sysfs_dirent *target_sd = NULL;
        struct sysfs_dirent *sd = NULL;
        struct sysfs_addrm_cxt acxt;
-       enum kobj_ns_type ns_type;
        int error;
 
        BUG_ON(!name || !parent_sd);
 
-       /* target->sd can go away beneath us but is protected with
-        * sysfs_assoc_lock.  Fetch target_sd from it.
+       /*
+        * We don't own @target and it may be removed at any time.
+        * Synchronize using sysfs_symlink_target_lock.  See
+        * sysfs_remove_dir() for details.
         */
-       spin_lock(&sysfs_assoc_lock);
+       spin_lock(&sysfs_symlink_target_lock);
        if (target->sd)
                target_sd = sysfs_get(target->sd);
-       spin_unlock(&sysfs_assoc_lock);
+       spin_unlock(&sysfs_symlink_target_lock);
 
        error = -ENOENT;
        if (!target_sd)
@@ -50,29 +51,15 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd,
        if (!sd)
                goto out_put;
 
-       ns_type = sysfs_ns_type(parent_sd);
-       if (ns_type)
-               sd->s_ns = target->ktype->namespace(target);
+       sd->s_ns = target_sd->s_ns;
        sd->s_symlink.target_sd = target_sd;
        target_sd = NULL;       /* reference is now owned by the symlink */
 
-       sysfs_addrm_start(&acxt, parent_sd);
-       /* Symlinks must be between directories with the same ns_type */
-       if (!ns_type ||
-           (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) {
-               if (warn)
-                       error = sysfs_add_one(&acxt, sd);
-               else
-                       error = __sysfs_add_one(&acxt, sd);
-       } else {
-               error = -EINVAL;
-               WARN(1, KERN_WARNING
-                       "sysfs: symlink across ns_types %s/%s -> %s/%s\n",
-                       parent_sd->s_name,
-                       sd->s_name,
-                       sd->s_symlink.target_sd->s_parent->s_name,
-                       sd->s_symlink.target_sd->s_name);
-       }
+       sysfs_addrm_start(&acxt);
+       if (warn)
+               error = sysfs_add_one(&acxt, sd, parent_sd);
+       else
+               error = __sysfs_add_one(&acxt, sd, parent_sd);
        sysfs_addrm_finish(&acxt);
 
        if (error)
@@ -155,11 +142,17 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ,
                        const char *name)
 {
        const void *ns = NULL;
-       spin_lock(&sysfs_assoc_lock);
-       if (targ->sd && sysfs_ns_type(kobj->sd))
+
+       /*
+        * We don't own @target and it may be removed at any time.
+        * Synchronize using sysfs_symlink_target_lock.  See
+        * sysfs_remove_dir() for details.
+        */
+       spin_lock(&sysfs_symlink_target_lock);
+       if (targ->sd)
                ns = targ->sd->s_ns;
-       spin_unlock(&sysfs_assoc_lock);
-       sysfs_hash_and_remove(kobj->sd, ns, name);
+       spin_unlock(&sysfs_symlink_target_lock);
+       sysfs_hash_and_remove(kobj->sd, name, ns);
 }
 
 /**
@@ -176,24 +169,25 @@ void sysfs_remove_link(struct kobject *kobj, const char *name)
        else
                parent_sd = kobj->sd;
 
-       sysfs_hash_and_remove(parent_sd, NULL, name);
+       sysfs_hash_and_remove(parent_sd, name, NULL);
 }
 EXPORT_SYMBOL_GPL(sysfs_remove_link);
 
 /**
- *     sysfs_rename_link - rename symlink in object's directory.
+ *     sysfs_rename_link_ns - rename symlink in object's directory.
  *     @kobj:  object we're acting for.
  *     @targ:  object we're pointing to.
  *     @old:   previous name of the symlink.
  *     @new:   new name of the symlink.
+ *     @new_ns: new namespace of the symlink.
  *
  *     A helper function for the common rename symlink idiom.
  */
-int sysfs_rename_link(struct kobject *kobj, struct kobject *targ,
-                       const char *old, const char *new)
+int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ,
+                        const char *old, const char *new, const void *new_ns)
 {
        struct sysfs_dirent *parent_sd, *sd = NULL;
-       const void *old_ns = NULL, *new_ns = NULL;
+       const void *old_ns = NULL;
        int result;
 
        if (!kobj)
@@ -205,7 +199,7 @@ int sysfs_rename_link(struct kobject *kobj, struct kobject *targ,
                old_ns = targ->sd->s_ns;
 
        result = -ENOENT;
-       sd = sysfs_get_dirent(parent_sd, old_ns, old);
+       sd = sysfs_get_dirent_ns(parent_sd, old, old_ns);
        if (!sd)
                goto out;
 
@@ -215,16 +209,13 @@ int sysfs_rename_link(struct kobject *kobj, struct kobject *targ,
        if (sd->s_symlink.target_sd->s_dir.kobj != targ)
                goto out;
 
-       if (sysfs_ns_type(parent_sd))
-               new_ns = targ->ktype->namespace(targ);
-
-       result = sysfs_rename(sd, parent_sd, new_ns, new);
+       result = sysfs_rename(sd, parent_sd, new, new_ns);
 
 out:
        sysfs_put(sd);
        return result;
 }
-EXPORT_SYMBOL_GPL(sysfs_rename_link);
+EXPORT_SYMBOL_GPL(sysfs_rename_link_ns);
 
 static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,
                                 struct sysfs_dirent *target_sd, char *path)
index b6deca3e301d2947c94c5789c8a941a0dfe497fa..e3aea92ebfa3668580ad2bab1ad80925be0f17a8 100644 (file)
@@ -29,15 +29,13 @@ struct sysfs_elem_symlink {
 };
 
 struct sysfs_elem_attr {
-       struct attribute        *attr;
+       union {
+               struct attribute        *attr;
+               struct bin_attribute    *bin_attr;
+       };
        struct sysfs_open_dirent *open;
 };
 
-struct sysfs_elem_bin_attr {
-       struct bin_attribute    *bin_attr;
-       struct hlist_head       buffers;
-};
-
 struct sysfs_inode_attrs {
        struct iattr    ia_iattr;
        void            *ia_secdata;
@@ -74,7 +72,6 @@ struct sysfs_dirent {
                struct sysfs_elem_dir           s_dir;
                struct sysfs_elem_symlink       s_symlink;
                struct sysfs_elem_attr          s_attr;
-               struct sysfs_elem_bin_attr      s_bin_attr;
        };
 
        unsigned short          s_flags;
@@ -93,11 +90,8 @@ struct sysfs_dirent {
 #define SYSFS_COPY_NAME                        (SYSFS_DIR | SYSFS_KOBJ_LINK)
 #define SYSFS_ACTIVE_REF               (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR)
 
-/* identify any namespace tag on sysfs_dirents */
-#define SYSFS_NS_TYPE_MASK             0xf00
-#define SYSFS_NS_TYPE_SHIFT            8
-
-#define SYSFS_FLAG_MASK                        ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK)
+#define SYSFS_FLAG_MASK                        ~SYSFS_TYPE_MASK
+#define SYSFS_FLAG_HAS_NS              0x01000
 #define SYSFS_FLAG_REMOVED             0x02000
 
 static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
@@ -105,16 +99,8 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
        return sd->s_flags & SYSFS_TYPE_MASK;
 }
 
-/*
- * Return any namespace tags on this dirent.
- * enum kobj_ns_type is defined in linux/kobject.h
- */
-static inline enum kobj_ns_type sysfs_ns_type(struct sysfs_dirent *sd)
-{
-       return (sd->s_flags & SYSFS_NS_TYPE_MASK) >> SYSFS_NS_TYPE_SHIFT;
-}
-
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
+
 #define sysfs_dirent_init_lockdep(sd)                          \
 do {                                                           \
        struct attribute *attr = sd->s_attr.attr;               \
@@ -124,15 +110,31 @@ do {                                                              \
                                                                \
        lockdep_init_map(&sd->dep_map, "s_active", key, 0);     \
 } while (0)
+
+/* Test for attributes that want to ignore lockdep for read-locking */
+static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd)
+{
+       int type = sysfs_type(sd);
+
+       return (type == SYSFS_KOBJ_ATTR || type == SYSFS_KOBJ_BIN_ATTR) &&
+               sd->s_attr.attr->ignore_lockdep;
+}
+
 #else
+
 #define sysfs_dirent_init_lockdep(sd) do {} while (0)
+
+static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd)
+{
+       return true;
+}
+
 #endif
 
 /*
  * Context structure to be used while adding/removing nodes.
  */
 struct sysfs_addrm_cxt {
-       struct sysfs_dirent     *parent_sd;
        struct sysfs_dirent     *removed;
 };
 
@@ -141,12 +143,13 @@ struct sysfs_addrm_cxt {
  */
 
 /*
- * Each sb is associated with a set of namespace tags (i.e.
- * the network namespace of the task which mounted this sysfs
- * instance).
+ * Each sb is associated with one namespace tag, currently the network
+ * namespace of the task which mounted this sysfs instance.  If multiple
+ * tags become necessary, make the following an array and compare
+ * sysfs_dirent tag against every entry.
  */
 struct sysfs_super_info {
-       void *ns[KOBJ_NS_TYPES];
+       void *ns;
 };
 #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info))
 extern struct sysfs_dirent sysfs_root;
@@ -156,38 +159,37 @@ extern struct kmem_cache *sysfs_dir_cachep;
  * dir.c
  */
 extern struct mutex sysfs_mutex;
-extern spinlock_t sysfs_assoc_lock;
+extern spinlock_t sysfs_symlink_target_lock;
 extern const struct dentry_operations sysfs_dentry_ops;
 
 extern const struct file_operations sysfs_dir_operations;
 extern const struct inode_operations sysfs_dir_inode_operations;
 
-struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd);
 struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd);
 void sysfs_put_active(struct sysfs_dirent *sd);
-void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
-                      struct sysfs_dirent *parent_sd);
-int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
-int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
-void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
+void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt);
+void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name);
+int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd,
+                   struct sysfs_dirent *parent_sd);
+int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd,
+                 struct sysfs_dirent *parent_sd);
+void sysfs_remove(struct sysfs_dirent *sd);
+int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name,
+                         const void *ns);
 void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt);
 
 struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
-                                      const void *ns,
-                                      const unsigned char *name);
-struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
-                                     const void *ns,
-                                     const unsigned char *name);
+                                      const unsigned char *name,
+                                      const void *ns);
 struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type);
 
 void release_sysfs_dirent(struct sysfs_dirent *sd);
 
 int sysfs_create_subdir(struct kobject *kobj, const char *name,
                        struct sysfs_dirent **p_sd);
-void sysfs_remove_subdir(struct sysfs_dirent *sd);
 
 int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd,
-                const void *ns, const char *new_name);
+                const char *new_name, const void *new_ns);
 
 static inline struct sysfs_dirent *__sysfs_get(struct sysfs_dirent *sd)
 {
@@ -218,25 +220,21 @@ int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
                  struct kstat *stat);
 int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
                   size_t size, int flags);
-int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns,
-                         const char *name);
 int sysfs_inode_init(void);
 
 /*
  * file.c
  */
 extern const struct file_operations sysfs_file_operations;
+extern const struct file_operations sysfs_bin_operations;
 
 int sysfs_add_file(struct sysfs_dirent *dir_sd,
                   const struct attribute *attr, int type);
 
-int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
-                       const struct attribute *attr, int type, umode_t amode);
-/*
- * bin.c
- */
-extern const struct file_operations bin_fops;
-void unmap_bin_file(struct sysfs_dirent *attr_sd);
+int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd,
+                          const struct attribute *attr, int type,
+                          umode_t amode, const void *ns);
+void sysfs_unmap_bin_file(struct sysfs_dirent *sd);
 
 /*
  * symlink.c
index be201ca2990ce1a0363608b39f2dd210097c57a2..00beddf6be20358ca7cd108b54a45902c4478c8b 100644 (file)
 #define ATMEL_US_IF            0x4c                    /* IrDA Filter Register */
 
 #define ATMEL_US_NAME          0xf0                    /* Ip Name */
+#define ATMEL_US_VERSION       0xfc                    /* Ip Version */
 
 #endif
index 263489d0788d02f2782b6d0a6dd4a504ba4da743..4d0b4d1aa1325256ae428294566257aaed04550a 100644 (file)
@@ -206,6 +206,12 @@ static inline struct dentry *debugfs_create_size_t(const char *name, umode_t mod
        return ERR_PTR(-ENODEV);
 }
 
+static inline struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode,
+                                    struct dentry *parent, atomic_t *value)
+{
+       return ERR_PTR(-ENODEV);
+}
+
 static inline struct dentry *debugfs_create_bool(const char *name, umode_t mode,
                                                 struct dentry *parent,
                                                 u32 *value)
@@ -227,6 +233,12 @@ static inline struct dentry *debugfs_create_regset32(const char *name,
        return ERR_PTR(-ENODEV);
 }
 
+static inline int debugfs_print_regs32(struct seq_file *s, const struct debugfs_reg32 *regs,
+                        int nregs, void __iomem *base, char *prefix)
+{
+       return 0;
+}
+
 static inline bool debugfs_initialized(void)
 {
        return false;
index 2a9d6ed5957903009d9ea7eea1050b2463b66b64..b025925df7f75a5b86fa894de93e2e83d0bdf571 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/atomic.h>
 #include <linux/ratelimit.h>
 #include <linux/uidgid.h>
+#include <linux/gfp.h>
 #include <asm/device.h>
 
 struct device;
@@ -63,9 +64,7 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
  * @name:      The name of the bus.
  * @dev_name:  Used for subsystems to enumerate devices like ("foo%u", dev->id).
  * @dev_root:  Default device to use as the parent.
- * @bus_attrs: Default attributes of the bus.
  * @dev_attrs: Default attributes of the devices on the bus.
- * @drv_attrs: Default attributes of the device drivers on the bus.
  * @bus_groups:        Default attributes of the bus.
  * @dev_groups:        Default attributes of the devices on the bus.
  * @drv_groups: Default attributes of the device drivers on the bus.
@@ -106,9 +105,7 @@ struct bus_type {
        const char              *name;
        const char              *dev_name;
        struct device           *dev_root;
-       struct bus_attribute    *bus_attrs;     /* use bus_groups instead */
        struct device_attribute *dev_attrs;     /* use dev_groups instead */
-       struct driver_attribute *drv_attrs;     /* use drv_groups instead */
        const struct attribute_group **bus_groups;
        const struct attribute_group **dev_groups;
        const struct attribute_group **drv_groups;
@@ -329,8 +326,6 @@ int subsys_virtual_register(struct bus_type *subsys,
  * @owner:     The module owner.
  * @class_attrs: Default attributes of this class.
  * @dev_groups:        Default attributes of the devices that belong to the class.
- * @dev_attrs: Default attributes of the devices belong to the class.
- * @dev_bin_attrs: Default binary attributes of the devices belong to the class.
  * @dev_kobj:  The kobject that represents this class and links it into the hierarchy.
  * @dev_uevent:        Called when a device is added, removed from this class, or a
  *             few other things that generate uevents to add the environment
@@ -358,9 +353,7 @@ struct class {
        struct module           *owner;
 
        struct class_attribute          *class_attrs;
-       struct device_attribute         *dev_attrs;     /* use dev_groups instead */
        const struct attribute_group    **dev_groups;
-       struct bin_attribute            *dev_bin_attrs;
        struct kobject                  *dev_kobj;
 
        int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
@@ -427,8 +420,6 @@ struct class_attribute {
                        char *buf);
        ssize_t (*store)(struct class *class, struct class_attribute *attr,
                        const char *buf, size_t count);
-       const void *(*namespace)(struct class *class,
-                                const struct class_attribute *attr);
 };
 
 #define CLASS_ATTR(_name, _mode, _show, _store) \
@@ -438,10 +429,24 @@ struct class_attribute {
 #define CLASS_ATTR_RO(_name) \
        struct class_attribute class_attr_##_name = __ATTR_RO(_name)
 
-extern int __must_check class_create_file(struct class *class,
-                                         const struct class_attribute *attr);
-extern void class_remove_file(struct class *class,
-                             const struct class_attribute *attr);
+extern int __must_check class_create_file_ns(struct class *class,
+                                            const struct class_attribute *attr,
+                                            const void *ns);
+extern void class_remove_file_ns(struct class *class,
+                                const struct class_attribute *attr,
+                                const void *ns);
+
+static inline int __must_check class_create_file(struct class *class,
+                                       const struct class_attribute *attr)
+{
+       return class_create_file_ns(class, attr, NULL);
+}
+
+static inline void class_remove_file(struct class *class,
+                                    const struct class_attribute *attr)
+{
+       return class_remove_file_ns(class, attr, NULL);
+}
 
 /* Simple class attribute that is just a static string */
 struct class_attribute_string {
@@ -602,8 +607,24 @@ extern void devres_close_group(struct device *dev, void *id);
 extern void devres_remove_group(struct device *dev, void *id);
 extern int devres_release_group(struct device *dev, void *id);
 
-/* managed kzalloc/kfree for device drivers, no kmalloc, always use kzalloc */
-extern void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp);
+/* managed devm_k.alloc/kfree for device drivers */
+extern void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp);
+static inline void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
+{
+       return devm_kmalloc(dev, size, gfp | __GFP_ZERO);
+}
+static inline void *devm_kmalloc_array(struct device *dev,
+                                      size_t n, size_t size, gfp_t flags)
+{
+       if (size != 0 && n > SIZE_MAX / size)
+               return NULL;
+       return devm_kmalloc(dev, n * size, flags);
+}
+static inline void *devm_kcalloc(struct device *dev,
+                                size_t n, size_t size, gfp_t flags)
+{
+       return devm_kmalloc_array(dev, n, size, flags | __GFP_ZERO);
+}
 extern void devm_kfree(struct device *dev, void *p);
 
 void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
@@ -1149,16 +1170,15 @@ do {                                                                    \
 #endif
 
 /*
- * dev_WARN*() acts like dev_printk(), but with the key difference
- * of using a WARN/WARN_ON to get the message out, including the
- * file/line information and a backtrace.
+ * dev_WARN*() acts like dev_printk(), but with the key difference of
+ * using WARN/WARN_ONCE to include file/line information and a backtrace.
  */
 #define dev_WARN(dev, format, arg...) \
-       WARN(1, "Device: %s\n" format, dev_driver_string(dev), ## arg);
+       WARN(1, "%s %s: " format, dev_driver_string(dev), dev_name(dev), ## arg);
 
 #define dev_WARN_ONCE(dev, condition, format, arg...) \
-       WARN_ONCE(condition, "Device %s\n" format, \
-                       dev_driver_string(dev), ## arg)
+       WARN_ONCE(condition, "%s %s: " format, \
+                       dev_driver_string(dev), dev_name(dev), ## arg)
 
 /* Create alias, so I can be autoloaded. */
 #define MODULE_ALIAS_CHARDEV(major,minor) \
index fcb51c88319f1a2b3d09715f9d74a12dc25cf49a..21c59af1150b57588fbe697c9ef9fe38aacff06f 100644 (file)
 enum extcon_cable_name {
        EXTCON_USB = 0,
        EXTCON_USB_HOST,
-       EXTCON_TA, /* Travel Adaptor */
+       EXTCON_TA,                      /* Travel Adaptor */
        EXTCON_FAST_CHARGER,
        EXTCON_SLOW_CHARGER,
-       EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */
+       EXTCON_CHARGE_DOWNSTREAM,       /* Charging an external device */
        EXTCON_HDMI,
        EXTCON_MHL,
        EXTCON_DVI,
@@ -76,8 +76,8 @@ struct extcon_cable;
 
 /**
  * struct extcon_dev - An extcon device represents one external connector.
- * @name:      The name of this extcon device. Parent device name is used
- *             if NULL.
+ * @name:              The name of this extcon device. Parent device name is
+ *                     used if NULL.
  * @supported_cable:   Array of supported cable names ending with NULL.
  *                     If supported_cable is NULL, cable name related APIs
  *                     are disabled.
@@ -89,21 +89,21 @@ struct extcon_cable;
  *                     be attached simulataneously. {0x7, 0} is equivalent to
  *                     {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there
  *                     can be no simultaneous connections.
- * @print_name:        An optional callback to override the method to print the
- *             name of the extcon device.
+ * @print_name:                An optional callback to override the method to print the
+ *                     name of the extcon device.
  * @print_state:       An optional callback to override the method to print the
- *             status of the extcon device.
- * @dev:       Device of this extcon. Do not provide at register-time.
- * @state:     Attach/detach state of this extcon. Do not provide at
- *             register-time
- * @nh:        Notifier for the state change events from this extcon
- * @entry:     To support list of extcon devices so that users can search
- *             for extcon devices based on the extcon name.
+ *                     status of the extcon device.
+ * @dev:               Device of this extcon.
+ * @state:             Attach/detach state of this extcon. Do not provide at
+ *                     register-time.
+ * @nh:                        Notifier for the state change events from this extcon
+ * @entry:             To support list of extcon devices so that users can search
+ *                     for extcon devices based on the extcon name.
  * @lock:
  * @max_supported:     Internal value to store the number of cables.
  * @extcon_dev_type:   Device_type struct to provide attribute_groups
  *                     customized for each extcon device.
- * @cables:    Sysfs subdirectories. Each represents one cable.
+ * @cables:            Sysfs subdirectories. Each represents one cable.
  *
  * In most cases, users only need to provide "User initializing data" of
  * this struct when registering an extcon. In some exceptional cases,
@@ -111,26 +111,27 @@ struct extcon_cable;
  * are overwritten by register function.
  */
 struct extcon_dev {
-       /* --- Optional user initializing data --- */
-       const char      *name;
+       /* Optional user initializing data */
+       const char *name;
        const char **supported_cable;
-       const u32       *mutually_exclusive;
+       const u32 *mutually_exclusive;
 
-       /* --- Optional callbacks to override class functions --- */
+       /* Optional callbacks to override class functions */
        ssize_t (*print_name)(struct extcon_dev *edev, char *buf);
        ssize_t (*print_state)(struct extcon_dev *edev, char *buf);
 
-       /* --- Internal data. Please do not set. --- */
-       struct device   *dev;
-       u32             state;
+       /* Internal data. Please do not set. */
+       struct device dev;
        struct raw_notifier_head nh;
        struct list_head entry;
-       spinlock_t lock; /* could be called by irq handler */
        int max_supported;
+       spinlock_t lock;        /* could be called by irq handler */
+       u32 state;
 
        /* /sys/class/extcon/.../cable.n/... */
        struct device_type extcon_dev_type;
        struct extcon_cable *cables;
+
        /* /sys/class/extcon/.../mutually_exclusive/... */
        struct attribute_group attr_g_muex;
        struct attribute **attrs_muex;
@@ -138,13 +139,13 @@ struct extcon_dev {
 };
 
 /**
- * struct extcon_cable - An internal data for each cable of extcon device.
- * @edev:      The extcon device
+ * struct extcon_cable - An internal data for each cable of extcon device.
+ * @edev:              The extcon device
  * @cable_index:       Index of this cable in the edev
- * @attr_g:    Attribute group for the cable
- * @attr_name: "name" sysfs entry
- * @attr_state:        "state" sysfs entry
- * @attrs:     Array pointing to attr_name and attr_state for attr_g
+ * @attr_g:            Attribute group for the cable
+ * @attr_name:         "name" sysfs entry
+ * @attr_state:                "state" sysfs entry
+ * @attrs:             Array pointing to attr_name and attr_state for attr_g
  */
 struct extcon_cable {
        struct extcon_dev *edev;
@@ -159,11 +160,13 @@ struct extcon_cable {
 
 /**
  * struct extcon_specific_cable_nb - An internal data for
- *                             extcon_register_interest().
- * @internal_nb:       a notifier block bridging extcon notifier and cable notifier.
- * @user_nb:   user provided notifier block for events from a specific cable.
+ *                                  extcon_register_interest().
+ * @internal_nb:       A notifier block bridging extcon notifier
+ *                     and cable notifier.
+ * @user_nb:           user provided notifier block for events from
+ *                     a specific cable.
  * @cable_index:       the target cable.
- * @edev:      the target extcon device.
+ * @edev:              the target extcon device.
  * @previous_value:    the saved previous event value.
  */
 struct extcon_specific_cable_nb {
@@ -180,7 +183,7 @@ struct extcon_specific_cable_nb {
  * Following APIs are for notifiers or configurations.
  * Notifiers are the external port and connection devices.
  */
-extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
+extern int extcon_dev_register(struct extcon_dev *edev);
 extern void extcon_dev_unregister(struct extcon_dev *edev);
 extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
 
@@ -238,8 +241,7 @@ extern int extcon_register_notifier(struct extcon_dev *edev,
 extern int extcon_unregister_notifier(struct extcon_dev *edev,
                                      struct notifier_block *nb);
 #else /* CONFIG_EXTCON */
-static inline int extcon_dev_register(struct extcon_dev *edev,
-                                     struct device *dev)
+static inline int extcon_dev_register(struct extcon_dev *edev)
 {
        return 0;
 }
index 20e9eef25d4c2d30e33fe4986e6eeb6276647bae..9ca958c4e94c79f9ae7997e47b6c521478930b19 100644 (file)
 
 /**
  * struct adc_jack_cond - condition to use an extcon state
- * @state      - the corresponding extcon state (if 0, this struct denotes
- *             the last adc_jack_cond element among the array)
- * @min_adc    - min adc value for this condition
- * @max_adc    - max adc value for this condition
+ * @state:             the corresponding extcon state (if 0, this struct
+ *                     denotes the last adc_jack_cond element among the array)
+ * @min_adc:           min adc value for this condition
+ * @max_adc:           max adc value for this condition
  *
  * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means
  * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and
  * because when no adc_jack_cond is met, state = 0 is automatically chosen.
  */
 struct adc_jack_cond {
-       u32 state; /* extcon state value. 0 if invalid */
+       u32 state;      /* extcon state value. 0 if invalid */
        u32 min_adc;
        u32 max_adc;
 };
 
 /**
  * struct adc_jack_pdata - platform data for adc jack device.
- * @name       - name of the extcon device. If null, "adc-jack" is used.
- * @consumer_channel - Unique name to identify the channel on the consumer
- *                   side. This typically describes the channels used within
- *                   the consumer. E.g. 'battery_voltage'
- * @cable_names        - array of cable names ending with null.
- * @adc_contitions     - array of struct adc_jack_cond conditions ending
- *                     with .state = 0 entry. This describes how to decode
- *                     adc values into extcon state.
- * @irq_flags  - irq flags used for the @irq
- * @handling_delay_ms  - in some devices, we need to read ADC value some
- *                     milli-seconds after the interrupt occurs. You may
- *                     describe such delays with @handling_delay_ms, which
- *                     is rounded-off by jiffies.
+ * @name:              name of the extcon device. If null, "adc-jack" is used.
+ * @consumer_channel Unique name to identify the channel on the consumer
+ *                     side. This typically describes the channels used within
+ *                     the consumer. E.g. 'battery_voltage'
+ * @cable_names:       array of cable names ending with null.
+ * @adc_contitions:    array of struct adc_jack_cond conditions ending
+ *                     with .state = 0 entry. This describes how to decode
+ *                     adc values into extcon state.
+ * @irq_flags:         irq flags used for the @irq
+ * @handling_delay_ms: in some devices, we need to read ADC value some
+ *                     milli-seconds after the interrupt occurs. You may
+ *                     describe such delays with @handling_delay_ms, which
+ *                     is rounded-off by jiffies.
  */
 struct adc_jack_pdata {
        const char *name;
        const char *consumer_channel;
-       /*
-        * The last entry should be NULL
-        */
+
+       /* The last entry should be NULL */
        const char **cable_names;
+
        /* The last entry's state should be 0 */
        struct adc_jack_cond *adc_conditions;
 
index 2d8307f7d67decd0f0729b1f121cf0475e4b04b7..4195810f87fe6a3eda2eaf928b3361cb795626b8 100644 (file)
 
 /**
  * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device.
- * @name       The name of this GPIO extcon device.
- * @gpio       Corresponding GPIO.
- * @debounce   Debounce time for GPIO IRQ in ms.
- * @irq_flags  IRQ Flags (e.g., IRQF_TRIGGER_LOW).
- * @state_on   print_state is overriden with state_on if attached. If Null,
- *             default method of extcon class is used.
- * @state_off  print_state is overriden with state_on if detached. If Null,
- *             default method of extcon class is used.
+ * @name:              The name of this GPIO extcon device.
+ * @gpio:              Corresponding GPIO.
+ * @gpio_active_low:   Boolean describing whether gpio active state is 1 or 0
+ *                     If true, low state of gpio means active.
+ *                     If false, high state of gpio means active.
+ * @debounce:          Debounce time for GPIO IRQ in ms.
+ * @irq_flags:         IRQ Flags (e.g., IRQF_TRIGGER_LOW).
+ * @state_on:          print_state is overriden with state_on if attached.
+ *                     If NULL, default method of extcon class is used.
+ * @state_off:         print_state is overriden with state_on if detached.
+ *                     If NUll, default method of extcon class is used.
  *
  * Note that in order for state_on or state_off to be valid, both state_on
  * and state_off should be not NULL. If at least one of them is NULL,
@@ -41,6 +44,7 @@
 struct gpio_extcon_platform_data {
        const char *name;
        unsigned gpio;
+       bool gpio_active_low;
        unsigned long debounce;
        unsigned long irq_flags;
 
index a6ac84871d6d415eb0e671b45daeddacbc7ad4a3..ff4e40cd45b1dcb15c66f1e178b655df7c40c0eb 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/atomic.h>
 #include <linux/compat.h>
+#include <linux/workqueue.h>
 #include <uapi/linux/filter.h>
 
 #ifdef CONFIG_COMPAT
@@ -25,15 +26,19 @@ struct sk_filter
 {
        atomic_t                refcnt;
        unsigned int            len;    /* Number of filter blocks */
+       struct rcu_head         rcu;
        unsigned int            (*bpf_func)(const struct sk_buff *skb,
                                            const struct sock_filter *filter);
-       struct rcu_head         rcu;
-       struct sock_filter      insns[0];
+       union {
+               struct sock_filter      insns[0];
+               struct work_struct      work;
+       };
 };
 
-static inline unsigned int sk_filter_len(const struct sk_filter *fp)
+static inline unsigned int sk_filter_size(unsigned int proglen)
 {
-       return fp->len * sizeof(struct sock_filter) + sizeof(*fp);
+       return max(sizeof(struct sk_filter),
+                  offsetof(struct sk_filter, insns[proglen]));
 }
 
 extern int sk_filter(struct sock *sk, struct sk_buff *skb);
@@ -67,11 +72,13 @@ static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
 }
 #define SK_RUN_FILTER(FILTER, SKB) (*FILTER->bpf_func)(SKB, FILTER->insns)
 #else
+#include <linux/slab.h>
 static inline void bpf_jit_compile(struct sk_filter *fp)
 {
 }
 static inline void bpf_jit_free(struct sk_filter *fp)
 {
+       kfree(fp);
 }
 #define SK_RUN_FILTER(FILTER, SKB) sk_run_filter(SKB, FILTER->insns)
 #endif
index d98503bde7e9bc7ace0e2c3ddc5f2ae019f527ff..15da677478ddc353b151d81362043bcbdfc3d089 100644 (file)
@@ -432,15 +432,6 @@ struct hv_ring_buffer_info {
        u32 ring_data_startoffset;
 };
 
-struct hv_ring_buffer_debug_info {
-       u32 current_interrupt_mask;
-       u32 current_read_index;
-       u32 current_write_index;
-       u32 bytes_avail_toread;
-       u32 bytes_avail_towrite;
-};
-
-
 /*
  *
  * hv_get_ringbuffer_availbytes()
@@ -902,23 +893,6 @@ enum vmbus_channel_state {
        CHANNEL_OPENED_STATE,
 };
 
-struct vmbus_channel_debug_info {
-       u32 relid;
-       enum vmbus_channel_state state;
-       uuid_le interfacetype;
-       uuid_le interface_instance;
-       u32 monitorid;
-       u32 servermonitor_pending;
-       u32 servermonitor_latency;
-       u32 servermonitor_connectionid;
-       u32 clientmonitor_pending;
-       u32 clientmonitor_latency;
-       u32 clientmonitor_connectionid;
-
-       struct hv_ring_buffer_debug_info inbound;
-       struct hv_ring_buffer_debug_info outbound;
-};
-
 /*
  * Represents each channel msg on the vmbus connection This is a
  * variable-size data structure depending on the msg type itself
@@ -1184,19 +1158,8 @@ extern int vmbus_recvpacket_raw(struct vmbus_channel *channel,
                                     u64 *requestid);
 
 
-extern void vmbus_get_debug_info(struct vmbus_channel *channel,
-                                    struct vmbus_channel_debug_info *debug);
-
 extern void vmbus_ontimer(unsigned long data);
 
-struct hv_dev_port_info {
-       u32 int_mask;
-       u32 read_idx;
-       u32 write_idx;
-       u32 bytes_avail_toread;
-       u32 bytes_avail_towrite;
-};
-
 /* Base driver object */
 struct hv_driver {
        const char *name;
index 81cbbdb96aae2bff8969fd68fefc086231de3c4b..673a3ce67f311df6567aa780dfdbf3d7adbe0cf9 100644 (file)
@@ -26,6 +26,7 @@
 #define __TWL_H_
 
 #include <linux/types.h>
+#include <linux/phy/phy.h>
 #include <linux/input/matrix_keypad.h>
 
 /*
@@ -615,6 +616,7 @@ enum twl4030_usb_mode {
 struct twl4030_usb_data {
        enum twl4030_usb_mode   usb_mode;
        unsigned long           features;
+       struct phy_init_data    *init_data;
 
        int             (*phy_init)(struct device *dev);
        int             (*phy_exit)(struct device *dev);
index b17974917dbf4369200901f119f670d14e0faa62..46a14229a162cf2bc5cc5e5b096c10c120766d8d 100644 (file)
@@ -1514,7 +1514,7 @@ static inline void ide_set_max_pio(ide_drive_t *drive)
 
 char *ide_media_string(ide_drive_t *);
 
-extern struct device_attribute ide_dev_attrs[];
+extern const struct attribute_group *ide_dev_groups[];
 extern struct bus_type ide_bus_type;
 extern struct class *ide_port_class;
 
index 19c19a5eee293396d0ddec9454e1ecb55adfed5b..f6c82de125413e8b6076ab91d68f64771641bfd1 100644 (file)
@@ -34,9 +34,9 @@ struct ipc_namespace {
        int             sem_ctls[4];
        int             used_sems;
 
-       int             msg_ctlmax;
-       int             msg_ctlmnb;
-       int             msg_ctlmni;
+       unsigned int    msg_ctlmax;
+       unsigned int    msg_ctlmnb;
+       unsigned int    msg_ctlmni;
        atomic_t        msg_bytes;
        atomic_t        msg_hdrs;
        int             auto_msgmni;
diff --git a/include/linux/kobj_completion.h b/include/linux/kobj_completion.h
new file mode 100644 (file)
index 0000000..a428f64
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _KOBJ_COMPLETION_H_
+#define _KOBJ_COMPLETION_H_
+
+#include <linux/kobject.h>
+#include <linux/completion.h>
+
+struct kobj_completion {
+       struct kobject kc_kobj;
+       struct completion kc_unregister;
+};
+
+#define kobj_to_kobj_completion(kobj) \
+       container_of(kobj, struct kobj_completion, kc_kobj)
+
+void kobj_completion_init(struct kobj_completion *kc, struct kobj_type *ktype);
+void kobj_completion_release(struct kobject *kobj);
+void kobj_completion_del_and_wait(struct kobj_completion *kc);
+#endif /* _KOBJ_COMPLETION_H_ */
index de6dcbcc6ef74335745943956cae0ba70bb35154..e7ba650086ce47078bbf64353bc63a2da5007ef1 100644 (file)
@@ -107,6 +107,7 @@ extern int __must_check kobject_move(struct kobject *, struct kobject *);
 extern struct kobject *kobject_get(struct kobject *kobj);
 extern void kobject_put(struct kobject *kobj);
 
+extern const void *kobject_namespace(struct kobject *kobj);
 extern char *kobject_get_path(struct kobject *kobj, gfp_t flag);
 
 struct kobj_type {
index 3de49aca451970a738b5ae55cfd74656248ac9ea..adf4070586d64be47fe5d18ed8f554e0c66670e1 100644 (file)
@@ -2264,11 +2264,12 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index)
 }
 
 #ifdef CONFIG_XPS
-extern int netif_set_xps_queue(struct net_device *dev, struct cpumask *mask,
+extern int netif_set_xps_queue(struct net_device *dev,
+                              const struct cpumask *mask,
                               u16 index);
 #else
 static inline int netif_set_xps_queue(struct net_device *dev,
-                                     struct cpumask *mask,
+                                     const struct cpumask *mask,
                                      u16 index)
 {
        return 0;
@@ -2873,8 +2874,20 @@ extern int __init dev_proc_init(void);
 #define dev_proc_init() 0
 #endif
 
-extern int netdev_class_create_file(struct class_attribute *class_attr);
-extern void netdev_class_remove_file(struct class_attribute *class_attr);
+extern int netdev_class_create_file_ns(struct class_attribute *class_attr,
+                                      const void *ns);
+extern void netdev_class_remove_file_ns(struct class_attribute *class_attr,
+                                       const void *ns);
+
+static inline int netdev_class_create_file(struct class_attribute *class_attr)
+{
+       return netdev_class_create_file_ns(class_attr, NULL);
+}
+
+static inline void netdev_class_remove_file(struct class_attribute *class_attr)
+{
+       netdev_class_remove_file_ns(class_attr, NULL);
+}
 
 extern struct kobj_ns_type_operations net_ns_type_operations;
 
index f3c7c24bec1ca99c89270611d87f9c264c3eb786..fbfdb9d8d3a7f59b4788bdfd1cdb26c86f0a81ca 100644 (file)
@@ -24,7 +24,8 @@ struct netpoll {
        struct net_device *dev;
        char dev_name[IFNAMSIZ];
        const char *name;
-       void (*rx_hook)(struct netpoll *, int, char *, int);
+       void (*rx_skb_hook)(struct netpoll *np, int source, struct sk_buff *skb,
+                           int offset, int len);
 
        union inet_addr local_ip, remote_ip;
        bool ipv6;
@@ -41,7 +42,7 @@ struct netpoll_info {
        unsigned long rx_flags;
        spinlock_t rx_lock;
        struct semaphore dev_lock;
-       struct list_head rx_np; /* netpolls that registered an rx_hook */
+       struct list_head rx_np; /* netpolls that registered an rx_skb_hook */
 
        struct sk_buff_head neigh_tx; /* list of neigh requests to reply to */
        struct sk_buff_head txq;
index cc88172c7d9a827f919592eba81275c052f9046b..c74088ab103b2aeb3e13497af1cd09db4f6f18fb 100644 (file)
@@ -332,7 +332,7 @@ do {                                                                        \
 #endif
 
 #ifndef this_cpu_sub
-# define this_cpu_sub(pcp, val)                this_cpu_add((pcp), -(val))
+# define this_cpu_sub(pcp, val)                this_cpu_add((pcp), -(typeof(pcp))(val))
 #endif
 
 #ifndef this_cpu_inc
@@ -418,7 +418,7 @@ do {                                                                        \
 # define this_cpu_add_return(pcp, val) __pcpu_size_call_return2(this_cpu_add_return_, pcp, val)
 #endif
 
-#define this_cpu_sub_return(pcp, val)  this_cpu_add_return(pcp, -(val))
+#define this_cpu_sub_return(pcp, val)  this_cpu_add_return(pcp, -(typeof(pcp))(val))
 #define this_cpu_inc_return(pcp)       this_cpu_add_return(pcp, 1)
 #define this_cpu_dec_return(pcp)       this_cpu_add_return(pcp, -1)
 
@@ -586,7 +586,7 @@ do {                                                                        \
 #endif
 
 #ifndef __this_cpu_sub
-# define __this_cpu_sub(pcp, val)      __this_cpu_add((pcp), -(val))
+# define __this_cpu_sub(pcp, val)      __this_cpu_add((pcp), -(typeof(pcp))(val))
 #endif
 
 #ifndef __this_cpu_inc
@@ -668,7 +668,7 @@ do {                                                                        \
        __pcpu_size_call_return2(__this_cpu_add_return_, pcp, val)
 #endif
 
-#define __this_cpu_sub_return(pcp, val)        __this_cpu_add_return(pcp, -(val))
+#define __this_cpu_sub_return(pcp, val)        __this_cpu_add_return(pcp, -(typeof(pcp))(val))
 #define __this_cpu_inc_return(pcp)     __this_cpu_add_return(pcp, 1)
 #define __this_cpu_dec_return(pcp)     __this_cpu_add_return(pcp, -1)
 
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
new file mode 100644 (file)
index 0000000..6d72269
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * phy.h -- generic phy header file
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __DRIVERS_PHY_H
+#define __DRIVERS_PHY_H
+
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+
+struct phy;
+
+/**
+ * struct phy_ops - set of function pointers for performing phy operations
+ * @init: operation to be performed for initializing phy
+ * @exit: operation to be performed while exiting
+ * @power_on: powering on the phy
+ * @power_off: powering off the phy
+ * @owner: the module owner containing the ops
+ */
+struct phy_ops {
+       int     (*init)(struct phy *phy);
+       int     (*exit)(struct phy *phy);
+       int     (*power_on)(struct phy *phy);
+       int     (*power_off)(struct phy *phy);
+       struct module *owner;
+};
+
+/**
+ * struct phy - represents the phy device
+ * @dev: phy device
+ * @id: id of the phy device
+ * @ops: function pointers for performing phy operations
+ * @init_data: list of PHY consumers (non-dt only)
+ * @mutex: mutex to protect phy_ops
+ * @init_count: used to protect when the PHY is used by multiple consumers
+ * @power_count: used to protect when the PHY is used by multiple consumers
+ */
+struct phy {
+       struct device           dev;
+       int                     id;
+       const struct phy_ops    *ops;
+       struct phy_init_data    *init_data;
+       struct mutex            mutex;
+       int                     init_count;
+       int                     power_count;
+};
+
+/**
+ * struct phy_provider - represents the phy provider
+ * @dev: phy provider device
+ * @owner: the module owner having of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy pointer
+ * @list: to maintain a linked list of PHY providers
+ */
+struct phy_provider {
+       struct device           *dev;
+       struct module           *owner;
+       struct list_head        list;
+       struct phy * (*of_xlate)(struct device *dev,
+               struct of_phandle_args *args);
+};
+
+/**
+ * struct phy_consumer - represents the phy consumer
+ * @dev_name: the device name of the controller that will use this PHY device
+ * @port: name given to the consumer port
+ */
+struct phy_consumer {
+       const char *dev_name;
+       const char *port;
+};
+
+/**
+ * struct phy_init_data - contains the list of PHY consumers
+ * @num_consumers: number of consumers for this PHY device
+ * @consumers: list of PHY consumers
+ */
+struct phy_init_data {
+       unsigned int num_consumers;
+       struct phy_consumer *consumers;
+};
+
+#define PHY_CONSUMER(_dev_name, _port)                         \
+{                                                              \
+       .dev_name       = _dev_name,                            \
+       .port           = _port,                                \
+}
+
+#define        to_phy(dev)     (container_of((dev), struct phy, dev))
+
+#define        of_phy_provider_register(dev, xlate)    \
+       __of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+#define        devm_of_phy_provider_register(dev, xlate)       \
+       __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+static inline void phy_set_drvdata(struct phy *phy, void *data)
+{
+       dev_set_drvdata(&phy->dev, data);
+}
+
+static inline void *phy_get_drvdata(struct phy *phy)
+{
+       return dev_get_drvdata(&phy->dev);
+}
+
+#if IS_ENABLED(CONFIG_GENERIC_PHY)
+int phy_pm_runtime_get(struct phy *phy);
+int phy_pm_runtime_get_sync(struct phy *phy);
+int phy_pm_runtime_put(struct phy *phy);
+int phy_pm_runtime_put_sync(struct phy *phy);
+void phy_pm_runtime_allow(struct phy *phy);
+void phy_pm_runtime_forbid(struct phy *phy);
+int phy_init(struct phy *phy);
+int phy_exit(struct phy *phy);
+int phy_power_on(struct phy *phy);
+int phy_power_off(struct phy *phy);
+struct phy *phy_get(struct device *dev, const char *string);
+struct phy *devm_phy_get(struct device *dev, const char *string);
+void phy_put(struct phy *phy);
+void devm_phy_put(struct device *dev, struct phy *phy);
+struct phy *of_phy_simple_xlate(struct device *dev,
+       struct of_phandle_args *args);
+struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
+       struct phy_init_data *init_data);
+struct phy *devm_phy_create(struct device *dev,
+       const struct phy_ops *ops, struct phy_init_data *init_data);
+void phy_destroy(struct phy *phy);
+void devm_phy_destroy(struct device *dev, struct phy *phy);
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args));
+struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args));
+void of_phy_provider_unregister(struct phy_provider *phy_provider);
+void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider);
+#else
+static inline int phy_pm_runtime_get(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_get_sync(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_put(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_put_sync(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline void phy_pm_runtime_allow(struct phy *phy)
+{
+       return;
+}
+
+static inline void phy_pm_runtime_forbid(struct phy *phy)
+{
+       return;
+}
+
+static inline int phy_init(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_exit(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_power_on(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_power_off(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline struct phy *phy_get(struct device *dev, const char *string)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *devm_phy_get(struct device *dev, const char *string)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void phy_put(struct phy *phy)
+{
+}
+
+static inline void devm_phy_put(struct device *dev, struct phy *phy)
+{
+}
+
+static inline struct phy *of_phy_simple_xlate(struct device *dev,
+       struct of_phandle_args *args)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *phy_create(struct device *dev,
+       const struct phy_ops *ops, struct phy_init_data *init_data)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *devm_phy_create(struct device *dev,
+       const struct phy_ops *ops, struct phy_init_data *init_data)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void phy_destroy(struct phy *phy)
+{
+}
+
+static inline void devm_phy_destroy(struct device *dev, struct phy *phy)
+{
+}
+
+static inline struct phy_provider *__of_phy_provider_register(
+       struct device *dev, struct module *owner, struct phy * (*of_xlate)(
+       struct device *dev, struct of_phandle_args *args))
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy_provider *__devm_of_phy_provider_register(struct device
+       *dev, struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+}
+
+static inline void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider)
+{
+}
+#endif
+
+#endif /* __DRIVERS_PHY_H */
index bf34e17cee7f6eb9c9068d811c37dae3b8c10fe0..c2fd9024717cb5aca213c1700126b69d6d350e24 100644 (file)
@@ -25,13 +25,4 @@ struct s5p_platform_mipi_csis {
        u8 hs_settle;
 };
 
-/**
- * s5p_csis_phy_enable - global MIPI-CSI receiver D-PHY control
- * @id:     MIPI-CSIS harware instance index (0...1)
- * @on:     true to enable D-PHY and deassert its reset
- *          false to disable D-PHY
- * @return: 0 on success, or negative error code on failure
- */
-int s5p_csis_phy_enable(int id, bool on);
-
 #endif /* __PLAT_SAMSUNG_MIPI_CSIS_H_ */
diff --git a/include/linux/platform_data/usb-ehci-s5p.h b/include/linux/platform_data/usb-ehci-s5p.h
deleted file mode 100644 (file)
index 5f28cae..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef __PLAT_SAMSUNG_EHCI_H
-#define __PLAT_SAMSUNG_EHCI_H __FILE__
-
-struct s5p_ehci_platdata {
-       int (*phy_init)(struct platform_device *pdev, int type);
-       int (*phy_exit)(struct platform_device *pdev, int type);
-};
-
-extern void s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd);
-
-#endif /* __PLAT_SAMSUNG_EHCI_H */
diff --git a/include/linux/platform_data/usb-ohci-exynos.h b/include/linux/platform_data/usb-ohci-exynos.h
deleted file mode 100644 (file)
index c256c59..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- *             http://www.samsung.com/
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef __MACH_EXYNOS_OHCI_H
-#define __MACH_EXYNOS_OHCI_H
-
-struct exynos4_ohci_platdata {
-       int (*phy_init)(struct platform_device *pdev, int type);
-       int (*phy_exit)(struct platform_device *pdev, int type);
-};
-
-extern void exynos4_ohci_set_platdata(struct exynos4_ohci_platdata *pd);
-
-#endif /* __MACH_EXYNOS_OHCI_H */
diff --git a/include/linux/platform_data/usb-rcar-gen2-phy.h b/include/linux/platform_data/usb-rcar-gen2-phy.h
new file mode 100644 (file)
index 0000000..dd3ba46
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __USB_RCAR_GEN2_PHY_H
+#define __USB_RCAR_GEN2_PHY_H
+
+#include <linux/types.h>
+
+struct rcar_gen2_phy_platform_data {
+       /* USB channel 0 configuration */
+       bool chan0_pci:1;       /* true: PCI USB host 0, false: USBHS */
+       /* USB channel 2 configuration */
+       bool chan2_pci:1;       /* true: PCI USB host 2, false: USBSS */
+};
+
+#endif
index ce8e4ffd78c773a1923c18cc53403726e79f71bf..16f6654082ddf82874a8531e51641d3bd62c1917 100644 (file)
@@ -178,6 +178,7 @@ struct platform_driver {
        int (*resume)(struct platform_device *);
        struct device_driver driver;
        const struct platform_device_id *id_table;
+       bool prevent_deferred_probe;
 };
 
 #define to_platform_driver(drv)        (container_of((drv), struct platform_driver, \
index e6131a782481186cb25ac1bb50f91bddc581e17d..694925837a1645bb7297cc9254efee7173a32197 100644 (file)
@@ -233,6 +233,8 @@ extern asmlinkage void dump_stack(void) __cold;
        no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
 #endif
 
+#include <linux/dynamic_debug.h>
+
 /* If you are writing a driver, please use dev_dbg instead */
 #if defined(CONFIG_DYNAMIC_DEBUG)
 /* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
@@ -343,7 +345,19 @@ extern asmlinkage void dump_stack(void) __cold;
 #endif
 
 /* If you are writing a driver, please use dev_dbg instead */
-#if defined(DEBUG)
+#if defined(CONFIG_DYNAMIC_DEBUG)
+/* descriptor check is first to prevent flooding with "callbacks suppressed" */
+#define pr_debug_ratelimited(fmt, ...)                                 \
+do {                                                                   \
+       static DEFINE_RATELIMIT_STATE(_rs,                              \
+                                     DEFAULT_RATELIMIT_INTERVAL,       \
+                                     DEFAULT_RATELIMIT_BURST);         \
+       DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);                 \
+       if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) &&        \
+           __ratelimit(&_rs))                                          \
+               __dynamic_pr_debug(&descriptor, fmt, ##__VA_ARGS__);    \
+} while (0)
+#elif defined(DEBUG)
 #define pr_debug_ratelimited(fmt, ...)                                 \
        printk_ratelimited(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
 #else
index b98291ac7f141f0b0cf33b569d7dde0796b60f8c..f729be981da0862a5ec09ee6a35e75312ae8768e 100644 (file)
@@ -66,7 +66,6 @@ struct uart_ops {
        void            (*set_ldisc)(struct uart_port *, int new);
        void            (*pm)(struct uart_port *, unsigned int state,
                              unsigned int oldstate);
-       int             (*set_wake)(struct uart_port *, unsigned int state);
 
        /*
         * Return a string describing the type of the port
index 11baec7c9b26dd4bfeb19ee4447c11cdda737a3c..6695040a031713ee257130e7295be38cc6aff293 100644 (file)
@@ -173,7 +173,6 @@ struct bin_attribute bin_attr_##_name = __BIN_ATTR_RW(_name, _size)
 struct sysfs_ops {
        ssize_t (*show)(struct kobject *, struct attribute *, char *);
        ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
-       const void *(*namespace)(struct kobject *, const struct attribute *);
 };
 
 struct sysfs_dirent;
@@ -183,19 +182,23 @@ struct sysfs_dirent;
 int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
                            void *data, struct module *owner);
 
-int __must_check sysfs_create_dir(struct kobject *kobj);
+int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns);
 void sysfs_remove_dir(struct kobject *kobj);
-int __must_check sysfs_rename_dir(struct kobject *kobj, const char *new_name);
-int __must_check sysfs_move_dir(struct kobject *kobj,
-                               struct kobject *new_parent_kobj);
-
-int __must_check sysfs_create_file(struct kobject *kobj,
-                                  const struct attribute *attr);
+int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
+                                    const void *new_ns);
+int __must_check sysfs_move_dir_ns(struct kobject *kobj,
+                                  struct kobject *new_parent_kobj,
+                                  const void *new_ns);
+
+int __must_check sysfs_create_file_ns(struct kobject *kobj,
+                                     const struct attribute *attr,
+                                     const void *ns);
 int __must_check sysfs_create_files(struct kobject *kobj,
                                   const struct attribute **attr);
 int __must_check sysfs_chmod_file(struct kobject *kobj,
                                  const struct attribute *attr, umode_t mode);
-void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr);
+void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
+                         const void *ns);
 void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr);
 
 int __must_check sysfs_create_bin_file(struct kobject *kobj,
@@ -210,8 +213,9 @@ int __must_check sysfs_create_link_nowarn(struct kobject *kobj,
                                          const char *name);
 void sysfs_remove_link(struct kobject *kobj, const char *name);
 
-int sysfs_rename_link(struct kobject *kobj, struct kobject *target,
-                       const char *old_name, const char *new_name);
+int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *target,
+                        const char *old_name, const char *new_name,
+                        const void *new_ns);
 
 void sysfs_delete_link(struct kobject *dir, struct kobject *targ,
                        const char *name);
@@ -241,9 +245,9 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
 
 void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr);
 void sysfs_notify_dirent(struct sysfs_dirent *sd);
-struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
-                                     const void *ns,
-                                     const unsigned char *name);
+struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd,
+                                        const unsigned char *name,
+                                        const void *ns);
 struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd);
 void sysfs_put(struct sysfs_dirent *sd);
 
@@ -257,7 +261,7 @@ static inline int sysfs_schedule_callback(struct kobject *kobj,
        return -ENOSYS;
 }
 
-static inline int sysfs_create_dir(struct kobject *kobj)
+static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
 {
        return 0;
 }
@@ -266,19 +270,22 @@ static inline void sysfs_remove_dir(struct kobject *kobj)
 {
 }
 
-static inline int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
+static inline int sysfs_rename_dir_ns(struct kobject *kobj,
+                                     const char *new_name, const void *new_ns)
 {
        return 0;
 }
 
-static inline int sysfs_move_dir(struct kobject *kobj,
-                                struct kobject *new_parent_kobj)
+static inline int sysfs_move_dir_ns(struct kobject *kobj,
+                                   struct kobject *new_parent_kobj,
+                                   const void *new_ns)
 {
        return 0;
 }
 
-static inline int sysfs_create_file(struct kobject *kobj,
-                                   const struct attribute *attr)
+static inline int sysfs_create_file_ns(struct kobject *kobj,
+                                      const struct attribute *attr,
+                                      const void *ns)
 {
        return 0;
 }
@@ -295,8 +302,9 @@ static inline int sysfs_chmod_file(struct kobject *kobj,
        return 0;
 }
 
-static inline void sysfs_remove_file(struct kobject *kobj,
-                                    const struct attribute *attr)
+static inline void sysfs_remove_file_ns(struct kobject *kobj,
+                                       const struct attribute *attr,
+                                       const void *ns)
 {
 }
 
@@ -333,8 +341,9 @@ static inline void sysfs_remove_link(struct kobject *kobj, const char *name)
 {
 }
 
-static inline int sysfs_rename_link(struct kobject *k, struct kobject *t,
-                                   const char *old_name, const char *new_name)
+static inline int sysfs_rename_link_ns(struct kobject *k, struct kobject *t,
+                                      const char *old_name,
+                                      const char *new_name, const void *ns)
 {
        return 0;
 }
@@ -413,10 +422,9 @@ static inline void sysfs_notify(struct kobject *kobj, const char *dir,
 static inline void sysfs_notify_dirent(struct sysfs_dirent *sd)
 {
 }
-static inline
-struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
-                                     const void *ns,
-                                     const unsigned char *name)
+static inline struct sysfs_dirent *
+sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, const unsigned char *name,
+                   const void *ns)
 {
        return NULL;
 }
@@ -435,4 +443,28 @@ static inline int __must_check sysfs_init(void)
 
 #endif /* CONFIG_SYSFS */
 
+static inline int __must_check sysfs_create_file(struct kobject *kobj,
+                                                const struct attribute *attr)
+{
+       return sysfs_create_file_ns(kobj, attr, NULL);
+}
+
+static inline void sysfs_remove_file(struct kobject *kobj,
+                                    const struct attribute *attr)
+{
+       return sysfs_remove_file_ns(kobj, attr, NULL);
+}
+
+static inline int sysfs_rename_link(struct kobject *kobj, struct kobject *target,
+                                   const char *old_name, const char *new_name)
+{
+       return sysfs_rename_link_ns(kobj, target, old_name, new_name, NULL);
+}
+
+static inline struct sysfs_dirent *
+sysfs_get_dirent(struct sysfs_dirent *parent_sd, const unsigned char *name)
+{
+       return sysfs_get_dirent_ns(parent_sd, name, NULL);
+}
+
 #endif /* _SYSFS_H_ */
index 7faf933cced7018657d2212d543530484ce17ec9..387fa7d05c982b758942f83395e328949324fc1a 100644 (file)
@@ -17,9 +17,6 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 
-/* Enable/disable SYSRQ support by default (0==no, 1==yes). */
-#define SYSRQ_DEFAULT_ENABLE   1
-
 /* Possible values of bitmask for enabling sysrq functions */
 /* 0x0001 is reserved for enable everything */
 #define SYSRQ_ENABLE_LOG       0x0002
diff --git a/include/linux/tc_act/tc_defact.h b/include/linux/tc_act/tc_defact.h
deleted file mode 100644 (file)
index 6f65d07..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef __LINUX_TC_DEF_H
-#define __LINUX_TC_DEF_H
-
-#include <linux/pkt_cls.h>
-
-struct tc_defact {
-       tc_gen;
-};
-                                                                                
-enum {
-       TCA_DEF_UNSPEC,
-       TCA_DEF_TM,
-       TCA_DEF_PARMS,
-       TCA_DEF_DATA,
-       __TCA_DEF_MAX
-};
-#define TCA_DEF_MAX (__TCA_DEF_MAX - 1)
-
-#endif
index 64f864651d8696aa5cf8a93b931c78c06a23bfdc..2f47989d8288b2fce1c0e000a1020cecb9b73f54 100644 (file)
@@ -180,7 +180,6 @@ struct tty_port_operations {
           IFF the port was initialized. Do not use to free resources. Called
           under the port mutex to serialize against activate/shutdowns */
        void (*shutdown)(struct tty_port *port);
-       void (*drop)(struct tty_port *port);
        /* Called under the port mutex from tty_port_open, serialized using
           the port mutex */
         /* FIXME: long term getting the tty argument *out* of this would be
index 001629cd1a97b831f2eb448013c114b3a76f0de7..7454865ad14834f50a05dc9baf7f6d78ea3de7b8 100644 (file)
@@ -475,7 +475,8 @@ struct usb3_lpm_parameters {
  * @lpm_capable: device supports LPM
  * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM
  * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM
- * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled
+ * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled
+ * @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled
  * @usb3_lpm_enabled: USB3 hardware LPM enabled
  * @string_langid: language ID for strings
  * @product: iProduct string, if present (static)
@@ -548,6 +549,7 @@ struct usb_device {
        unsigned usb2_hw_lpm_capable:1;
        unsigned usb2_hw_lpm_besl_capable:1;
        unsigned usb2_hw_lpm_enabled:1;
+       unsigned usb2_hw_lpm_allowed:1;
        unsigned usb3_lpm_enabled:1;
        int string_langid;
 
@@ -702,7 +704,7 @@ extern int usb_alloc_streams(struct usb_interface *interface,
                unsigned int num_streams, gfp_t mem_flags);
 
 /* Reverts a group of bulk endpoints back to not using stream IDs. */
-extern void usb_free_streams(struct usb_interface *interface,
+extern int usb_free_streams(struct usb_interface *interface,
                struct usb_host_endpoint **eps, unsigned int num_eps,
                gfp_t mem_flags);
 
@@ -1209,11 +1211,13 @@ struct usb_anchor {
        struct list_head urb_list;
        wait_queue_head_t wait;
        spinlock_t lock;
+       atomic_t suspend_wakeups;
        unsigned int poisoned:1;
 };
 
 static inline void init_usb_anchor(struct usb_anchor *anchor)
 {
+       memset(anchor, 0, sizeof(*anchor));
        INIT_LIST_HEAD(&anchor->urb_list);
        init_waitqueue_head(&anchor->wait);
        spin_lock_init(&anchor->lock);
@@ -1574,6 +1578,8 @@ extern void usb_kill_anchored_urbs(struct usb_anchor *anchor);
 extern void usb_poison_anchored_urbs(struct usb_anchor *anchor);
 extern void usb_unpoison_anchored_urbs(struct usb_anchor *anchor);
 extern void usb_unlink_anchored_urbs(struct usb_anchor *anchor);
+extern void usb_anchor_suspend_wakeups(struct usb_anchor *anchor);
+extern void usb_anchor_resume_wakeups(struct usb_anchor *anchor);
 extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor);
 extern void usb_unanchor_urb(struct urb *urb);
 extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
index 75efc45eaa2ff361d4cb92fa4c325eb8d5d50461..b8aba196f7f12948cbf4d2a587371b081f150d1c 100644 (file)
@@ -73,6 +73,7 @@ struct giveback_urb_bh {
        spinlock_t lock;
        struct list_head  head;
        struct tasklet_struct bh;
+       struct usb_host_endpoint *completing_ep;
 };
 
 struct usb_hcd {
@@ -140,6 +141,7 @@ struct usb_hcd {
        unsigned                wireless:1;     /* Wireless 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 int            irq;            /* irq allocated */
        void __iomem            *regs;          /* device memory/io */
@@ -378,6 +380,12 @@ static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
        return hcd->driver->flags & HCD_BH;
 }
 
+static inline bool hcd_periodic_completion_in_progress(struct usb_hcd *hcd,
+               struct usb_host_endpoint *ep)
+{
+       return hcd->high_prio_bh.completing_ep == ep;
+}
+
 extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
 extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
                int status);
@@ -428,6 +436,8 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev,
 extern void usb_hcd_pci_remove(struct pci_dev *dev);
 extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
 
+extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev);
+
 #ifdef CONFIG_PM
 extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
 #endif
@@ -496,6 +506,7 @@ struct usb_tt {
        struct usb_device       *hub;   /* upstream highspeed hub */
        int                     multi;  /* true means one TT per port */
        unsigned                think_time;     /* think time in ns */
+       void                    *hcpriv;        /* HCD private data */
 
        /* for control/bulk error recovery (CLEAR_TT_BUFFER) */
        spinlock_t              lock;
@@ -554,9 +565,8 @@ extern void usb_ep0_reinit(struct usb_device *);
                 * of (7/6 * 8 * bytecount) = 9.33 * bytecount */
                /* bytecount = data payload byte count */
 
-#define NS_TO_US(ns)   ((ns + 500L) / 1000L)
-                       /* convert & round nanoseconds to microseconds */
-
+#define NS_TO_US(ns)   DIV_ROUND_UP(ns, 1000L)
+                       /* convert nanoseconds to microseconds, rounding up */
 
 /*
  * Full/low speed bandwidth allocation constants/support.
diff --git a/include/linux/usb/intel_mid_otg.h b/include/linux/usb/intel_mid_otg.h
deleted file mode 100644 (file)
index 756cf55..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Intel MID (Langwell/Penwell) USB OTG Transceiver driver
- * Copyright (C) 2008 - 2010, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#ifndef __INTEL_MID_OTG_H
-#define __INTEL_MID_OTG_H
-
-#include <linux/pm.h>
-#include <linux/usb/otg.h>
-#include <linux/notifier.h>
-
-struct intel_mid_otg_xceiv;
-
-/* This is a common data structure for Intel MID platform to
- * save values of the OTG state machine */
-struct otg_hsm {
-       /* Input */
-       int a_bus_resume;
-       int a_bus_suspend;
-       int a_conn;
-       int a_sess_vld;
-       int a_srp_det;
-       int a_vbus_vld;
-       int b_bus_resume;
-       int b_bus_suspend;
-       int b_conn;
-       int b_se0_srp;
-       int b_ssend_srp;
-       int b_sess_end;
-       int b_sess_vld;
-       int id;
-/* id values */
-#define ID_B           0x05
-#define ID_A           0x04
-#define ID_ACA_C       0x03
-#define ID_ACA_B       0x02
-#define ID_ACA_A       0x01
-       int power_up;
-       int adp_change;
-       int test_device;
-
-       /* Internal variables */
-       int a_set_b_hnp_en;
-       int b_srp_done;
-       int b_hnp_enable;
-       int hnp_poll_enable;
-
-       /* Timeout indicator for timers */
-       int a_wait_vrise_tmout;
-       int a_wait_bcon_tmout;
-       int a_aidl_bdis_tmout;
-       int a_bidl_adis_tmout;
-       int a_bidl_adis_tmr;
-       int a_wait_vfall_tmout;
-       int b_ase0_brst_tmout;
-       int b_bus_suspend_tmout;
-       int b_srp_init_tmout;
-       int b_srp_fail_tmout;
-       int b_srp_fail_tmr;
-       int b_adp_sense_tmout;
-
-       /* Informative variables */
-       int a_bus_drop;
-       int a_bus_req;
-       int a_clr_err;
-       int b_bus_req;
-       int a_suspend_req;
-       int b_bus_suspend_vld;
-
-       /* Output */
-       int drv_vbus;
-       int loc_conn;
-       int loc_sof;
-
-       /* Others */
-       int vbus_srp_up;
-};
-
-/* must provide ULPI access function to read/write registers implemented in
- * ULPI address space */
-struct iotg_ulpi_access_ops {
-       int     (*read)(struct intel_mid_otg_xceiv *iotg, u8 reg, u8 *val);
-       int     (*write)(struct intel_mid_otg_xceiv *iotg, u8 reg, u8 val);
-};
-
-#define OTG_A_DEVICE   0x0
-#define OTG_B_DEVICE   0x1
-
-/*
- * the Intel MID (Langwell/Penwell) otg transceiver driver needs to interact
- * with device and host drivers to implement the USB OTG related feature. More
- * function members are added based on usb_phy data structure for this
- * purpose.
- */
-struct intel_mid_otg_xceiv {
-       struct usb_phy          otg;
-       struct otg_hsm          hsm;
-
-       /* base address */
-       void __iomem            *base;
-
-       /* ops to access ulpi */
-       struct iotg_ulpi_access_ops     ulpi_ops;
-
-       /* atomic notifier for interrupt context */
-       struct atomic_notifier_head     iotg_notifier;
-
-       /* start/stop USB Host function */
-       int     (*start_host)(struct intel_mid_otg_xceiv *iotg);
-       int     (*stop_host)(struct intel_mid_otg_xceiv *iotg);
-
-       /* start/stop USB Peripheral function */
-       int     (*start_peripheral)(struct intel_mid_otg_xceiv *iotg);
-       int     (*stop_peripheral)(struct intel_mid_otg_xceiv *iotg);
-
-       /* start/stop ADP sense/probe function */
-       int     (*set_adp_probe)(struct intel_mid_otg_xceiv *iotg,
-                                       bool enabled, int dev);
-       int     (*set_adp_sense)(struct intel_mid_otg_xceiv *iotg,
-                                       bool enabled);
-
-#ifdef CONFIG_PM
-       /* suspend/resume USB host function */
-       int     (*suspend_host)(struct intel_mid_otg_xceiv *iotg,
-                                       pm_message_t message);
-       int     (*resume_host)(struct intel_mid_otg_xceiv *iotg);
-
-       int     (*suspend_peripheral)(struct intel_mid_otg_xceiv *iotg,
-                                       pm_message_t message);
-       int     (*resume_peripheral)(struct intel_mid_otg_xceiv *iotg);
-#endif
-
-};
-static inline
-struct intel_mid_otg_xceiv *otg_to_mid_xceiv(struct usb_phy *otg)
-{
-       return container_of(otg, struct intel_mid_otg_xceiv, otg);
-}
-
-#define MID_OTG_NOTIFY_CONNECT         0x0001
-#define MID_OTG_NOTIFY_DISCONN         0x0002
-#define MID_OTG_NOTIFY_HSUSPEND                0x0003
-#define MID_OTG_NOTIFY_HRESUME         0x0004
-#define MID_OTG_NOTIFY_CSUSPEND                0x0005
-#define MID_OTG_NOTIFY_CRESUME         0x0006
-#define MID_OTG_NOTIFY_HOSTADD         0x0007
-#define MID_OTG_NOTIFY_HOSTREMOVE      0x0008
-#define MID_OTG_NOTIFY_CLIENTADD       0x0009
-#define MID_OTG_NOTIFY_CLIENTREMOVE    0x000a
-
-static inline int
-intel_mid_otg_register_notifier(struct intel_mid_otg_xceiv *iotg,
-                               struct notifier_block *nb)
-{
-       return atomic_notifier_chain_register(&iotg->iotg_notifier, nb);
-}
-
-static inline void
-intel_mid_otg_unregister_notifier(struct intel_mid_otg_xceiv *iotg,
-                               struct notifier_block *nb)
-{
-       atomic_notifier_chain_unregister(&iotg->iotg_notifier, nb);
-}
-
-#endif /* __INTEL_MID_OTG_H */
index 053c26841cc39bde245acac370e152189f256814..eb505250940af073cd1059f82c4e48d773df3acb 100644 (file)
@@ -99,8 +99,6 @@ struct musb_hdrc_platform_data {
        /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
        u8              mode;
 
-       u8              has_mailbox:1;
-
        /* for clk_get() */
        const char      *clock;
 
index 27b5b8c931b0bd70d1e20d636b08beb6ebe3dc64..596b01918813aaeffbeb170001cb370ae9f5f776 100644 (file)
 #ifndef __OMAP_CONTROL_USB_H__
 #define __OMAP_CONTROL_USB_H__
 
+enum omap_control_usb_type {
+       OMAP_CTRL_TYPE_OTGHS = 1,       /* Mailbox OTGHS_CONTROL */
+       OMAP_CTRL_TYPE_USB2,    /* USB2_PHY, power down in CONTROL_DEV_CONF */
+       OMAP_CTRL_TYPE_PIPE3,   /* PIPE3 PHY, DPLL & seperate Rx/Tx power */
+       OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
+};
+
 struct omap_control_usb {
        struct device *dev;
 
-       u32 __iomem *dev_conf;
        u32 __iomem *otghs_control;
-       u32 __iomem *phy_power;
+       u32 __iomem *power;
+       u32 __iomem *power_aux;
 
        struct clk *sys_clk;
 
-       u32 type;
-};
-
-struct omap_control_usb_platform_data {
-       u8 type;
+       enum omap_control_usb_type type;
 };
 
 enum omap_control_usb_mode {
@@ -42,10 +45,6 @@ enum omap_control_usb_mode {
        USB_MODE_DISCONNECT,
 };
 
-/* To differentiate ctrl module IP having either mailbox or USB3 PHY power */
-#define        OMAP_CTRL_DEV_TYPE1             0x1
-#define        OMAP_CTRL_DEV_TYPE2             0x2
-
 #define        OMAP_CTRL_DEV_PHY_PD            BIT(0)
 
 #define        OMAP_CTRL_DEV_AVALID            BIT(0)
@@ -63,26 +62,18 @@ enum omap_control_usb_mode {
 #define        OMAP_CTRL_USB3_PHY_TX_RX_POWERON        0x3
 #define        OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF       0x0
 
+#define OMAP_CTRL_USB2_PHY_PD          BIT(28)
+
 #if IS_ENABLED(CONFIG_OMAP_CONTROL_USB)
-extern struct device *omap_get_control_dev(void);
 extern void omap_control_usb_phy_power(struct device *dev, int on);
-extern void omap_control_usb3_phy_power(struct device *dev, bool on);
 extern void omap_control_usb_set_mode(struct device *dev,
        enum omap_control_usb_mode mode);
 #else
-static inline struct device *omap_get_control_dev(void)
-{
-       return ERR_PTR(-ENODEV);
-}
 
 static inline void omap_control_usb_phy_power(struct device *dev, int on)
 {
 }
 
-static inline void omap_control_usb3_phy_power(struct device *dev, int on)
-{
-}
-
 static inline void omap_control_usb_set_mode(struct device *dev,
        enum omap_control_usb_mode mode)
 {
index d528b804515081f9d5388dc565822241cfd6adc9..704a1ab8240ca124f29c5ce361c871090d28ea5b 100644 (file)
@@ -320,6 +320,8 @@ extern struct usb_serial_port *usb_serial_port_get_by_minor(unsigned int minor);
 extern void usb_serial_put(struct usb_serial *serial);
 extern int usb_serial_generic_open(struct tty_struct *tty,
        struct usb_serial_port *port);
+extern int usb_serial_generic_write_start(struct usb_serial_port *port,
+                                                       gfp_t mem_flags);
 extern int usb_serial_generic_write(struct tty_struct *tty,
        struct usb_serial_port *port, const unsigned char *buf, int count);
 extern void usb_serial_generic_close(struct usb_serial_port *port);
index 11d85b9c1b081af6a9f4f30ac3c1458766ed4238..cc8d818a83be40f58ae5ce29a60b08ca9b0ea8f5 100644 (file)
@@ -9,7 +9,8 @@ struct usb_phy_gen_xceiv_platform_data {
 
        /* if set fails with -EPROBE_DEFER if can't get regulator */
        unsigned int needs_vcc:1;
-       unsigned int needs_reset:1;
+       unsigned int needs_reset:1;     /* deprecated */
+       int gpio_reset;
 };
 
 #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE))
index 4ff744e2b678a27f051649b86f1c8ee4eb8210d1..c1257130769b5465a04d7d162207613f378acfde 100644 (file)
@@ -142,7 +142,7 @@ enum wa_notif_type {
 struct wa_notif_hdr {
        u8 bLength;
        u8 bNotifyType;                 /* enum wa_notif_type */
-} __attribute__((packed));
+} __packed;
 
 /**
  * HWA DN Received notification [(WUSB] section 8.5.4.2)
@@ -158,7 +158,7 @@ struct hwa_notif_dn {
        u8 bSourceDeviceAddr;           /* from errata 2005/07 */
        u8 bmAttributes;
        struct wusb_dn_hdr dndata[];
-} __attribute__((packed));
+} __packed;
 
 /* [WUSB] section 8.3.3 */
 enum wa_xfer_type {
@@ -167,6 +167,8 @@ enum wa_xfer_type {
        WA_XFER_TYPE_ISO = 0x82,
        WA_XFER_RESULT = 0x83,
        WA_XFER_ABORT = 0x84,
+       WA_XFER_ISO_PACKET_INFO = 0xA0,
+       WA_XFER_ISO_PACKET_STATUS = 0xA1,
 };
 
 /* [WUSB] section 8.3.3 */
@@ -177,28 +179,47 @@ struct wa_xfer_hdr {
        __le32 dwTransferID;            /* Host-assigned ID */
        __le32 dwTransferLength;        /* Length of data to xfer */
        u8 bTransferSegment;
-} __attribute__((packed));
+} __packed;
 
 struct wa_xfer_ctl {
        struct wa_xfer_hdr hdr;
        u8 bmAttribute;
        __le16 wReserved;
        struct usb_ctrlrequest baSetupData;
-} __attribute__((packed));
+} __packed;
 
 struct wa_xfer_bi {
        struct wa_xfer_hdr hdr;
        u8 bReserved;
        __le16 wReserved;
-} __attribute__((packed));
+} __packed;
 
+/* [WUSB] section 8.5.5 */
 struct wa_xfer_hwaiso {
        struct wa_xfer_hdr hdr;
        u8 bReserved;
        __le16 wPresentationTime;
        __le32 dwNumOfPackets;
-       /* FIXME: u8 pktdata[]? */
-} __attribute__((packed));
+} __packed;
+
+struct wa_xfer_packet_info_hwaiso {
+       __le16 wLength;
+       u8 bPacketType;
+       u8 bReserved;
+       __le16 PacketLength[0];
+} __packed;
+
+struct wa_xfer_packet_status_len_hwaiso {
+       __le16 PacketLength;
+       __le16 PacketStatus;
+} __packed;
+
+struct wa_xfer_packet_status_hwaiso {
+       __le16 wLength;
+       u8 bPacketType;
+       u8 bReserved;
+       struct wa_xfer_packet_status_len_hwaiso PacketStatus[0];
+} __packed;
 
 /* [WUSB] section 8.3.3.5 */
 struct wa_xfer_abort {
@@ -206,7 +227,7 @@ struct wa_xfer_abort {
        u8 bRequestType;
        __le16 wRPipe;                  /* RPipe index */
        __le32 dwTransferID;            /* Host-assigned ID */
-} __attribute__((packed));
+} __packed;
 
 /**
  * WA Transfer Complete notification ([WUSB] section 8.3.3.3)
@@ -216,7 +237,7 @@ struct wa_notif_xfer {
        struct wa_notif_hdr hdr;
        u8 bEndpoint;
        u8 Reserved;
-} __attribute__((packed));
+} __packed;
 
 /** Transfer result basic codes [WUSB] table 8-15 */
 enum {
@@ -243,7 +264,7 @@ struct wa_xfer_result {
        u8     bTransferSegment;
        u8     bTransferStatus;
        __le32 dwNumOfPackets;
-} __attribute__((packed));
+} __packed;
 
 /**
  * Wire Adapter Class Descriptor ([WUSB] section 8.5.2.7).
@@ -258,16 +279,16 @@ struct wa_xfer_result {
 struct usb_wa_descriptor {
        u8      bLength;
        u8      bDescriptorType;
-       u16     bcdWAVersion;
+       __le16  bcdWAVersion;
        u8      bNumPorts;              /* don't use!! */
        u8      bmAttributes;           /* Reserved == 0 */
-       u16     wNumRPipes;
-       u16     wRPipeMaxBlock;
+       __le16  wNumRPipes;
+       __le16  wRPipeMaxBlock;
        u8      bRPipeBlockSize;
        u8      bPwrOn2PwrGood;
        u8      bNumMMCIEs;
        u8      DeviceRemovable;        /* FIXME: in DWA this is up to 16 bytes */
-} __attribute__((packed));
+} __packed;
 
 /**
  * HWA Device Information Buffer (WUSB1.0[T8.54])
@@ -277,6 +298,6 @@ struct hwa_dev_info {
        u8      bDeviceAddress;
        __le16  wPHYRates;
        u8      bmDeviceAttribute;
-} __attribute__((packed));
+} __packed;
 
 #endif /* #ifndef __LINUX_USB_WUSB_WA_H */
index 7fe28228b2742efc0c9eb69c7bbc866f0194ef19..512cdc2fb80f40f99bb1b09fe1d0d4dcfeab345c 100644 (file)
@@ -77,6 +77,6 @@ struct yamdrv_ioctl_cfg {
 
 struct yamdrv_ioctl_mcs {
        int cmd;
-       int bitrate;
+       unsigned int bitrate;
        unsigned char bits[YAM_FPGA_SIZE];
 };
index a7a683e30b64e6beb2bc87907c85576d85385007..a8c2ef6d3b932abbb42e28be158d472effd02513 100644 (file)
@@ -290,6 +290,7 @@ static inline int cipso_v4_validate(const struct sk_buff *skb,
        unsigned char err_offset = 0;
        u8 opt_len = opt[1];
        u8 opt_iter;
+       u8 tag_len;
 
        if (opt_len < 8) {
                err_offset = 1;
@@ -302,11 +303,12 @@ static inline int cipso_v4_validate(const struct sk_buff *skb,
        }
 
        for (opt_iter = 6; opt_iter < opt_len;) {
-               if (opt[opt_iter + 1] > (opt_len - opt_iter)) {
+               tag_len = opt[opt_iter + 1];
+               if ((tag_len == 0) || (opt[opt_iter + 1] > (opt_len - opt_iter))) {
                        err_offset = opt_iter + 1;
                        goto out;
                }
-               opt_iter += opt[opt_iter + 1];
+               opt_iter += tag_len;
        }
 
 out:
index 3bc4865f82679137077db4dd82c177bd39b51515..3c4c944096c9e5133695a415b530c80908980f39 100644 (file)
@@ -479,10 +479,22 @@ static inline struct dst_entry *xfrm_lookup(struct net *net,
 {
        return dst_orig;
 } 
+
+static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
+{
+       return NULL;
+}
+
 #else
 extern struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
                                     const struct flowi *fl, struct sock *sk,
                                     int flags);
+
+/* skb attached with this dst needs transformation if dst->xfrm is valid */
+static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
+{
+       return dst->xfrm;
+}
 #endif
 
 #endif /* _NET_DST_H */
index 48ec25a7fcb60a09a2c323c70221a31853a82261..5e661a979694e87d2679e395aa0dce0da033942f 100644 (file)
@@ -165,6 +165,7 @@ static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst)
 static inline void rt6_clean_expires(struct rt6_info *rt)
 {
        rt->rt6i_flags &= ~RTF_EXPIRES;
+       rt->dst.expires = 0;
 }
 
 static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires)
index f525e7038cca4eaae4fd7b011ae1f97072ddffc9..2b786b7e35850fe4b4b147a8a0cdcf0b010d2cb9 100644 (file)
@@ -194,11 +194,9 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
               skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
 }
 
-static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt, struct in6_addr *dest)
+static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt)
 {
-       if (rt->rt6i_flags & RTF_GATEWAY)
-               return &rt->rt6i_gateway;
-       return dest;
+       return &rt->rt6i_gateway;
 }
 
 #endif
index d0d11df9cba1ca3b4f8196071f8a0beed8b32f25..807d6b7a943fecab78db1afd6b57ee33b70ded72 100644 (file)
@@ -133,7 +133,7 @@ struct ieee802154_ops {
 
 /* Basic interface to register ieee802154 device */
 struct ieee802154_dev *
-ieee802154_alloc_device(size_t priv_data_lex, struct ieee802154_ops *ops);
+ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops);
 void ieee802154_free_device(struct ieee802154_dev *dev);
 int ieee802154_register_device(struct ieee802154_dev *dev);
 void ieee802154_unregister_device(struct ieee802154_dev *dev);
index 1d37a8086bed53575fedc30b9c1db750d4cd2822..808cbc2ec6c1b38ea82198d2171079863377777d 100644 (file)
@@ -1630,16 +1630,14 @@ static inline void sk_filter_release(struct sk_filter *fp)
 
 static inline void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp)
 {
-       unsigned int size = sk_filter_len(fp);
-
-       atomic_sub(size, &sk->sk_omem_alloc);
+       atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc);
        sk_filter_release(fp);
 }
 
 static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp)
 {
        atomic_inc(&fp->refcnt);
-       atomic_add(sk_filter_len(fp), &sk->sk_omem_alloc);
+       atomic_add(sk_filter_size(fp->len), &sk->sk_omem_alloc);
 }
 
 /*
index fe66533e9b7a51ef143acd33ff398bee47f71398..fb0a312bcb810c93622028ef93934d3779189142 100644 (file)
@@ -68,6 +68,7 @@ struct rsnd_scu_platform_info {
  *
  * A : generation
  */
+#define RSND_GEN_MASK  (0xF << 0)
 #define RSND_GEN1      (1 << 0) /* fixme */
 #define RSND_GEN2      (2 << 0) /* fixme */
 
index aef8fc3540254c8796015d016a8e326eb71ed054..da9cc0f05c93843e713923fc1b5d063e7d444cae 100644 (file)
@@ -144,7 +144,7 @@ TRACE_EVENT(target_sequencer_start,
        ),
 
        TP_fast_assign(
-               __entry->unpacked_lun   = cmd->se_lun->unpacked_lun;
+               __entry->unpacked_lun   = cmd->orig_fe_lun;
                __entry->opcode         = cmd->t_task_cdb[0];
                __entry->data_length    = cmd->data_length;
                __entry->task_attribute = cmd->sam_task_attr;
@@ -182,7 +182,7 @@ TRACE_EVENT(target_cmd_complete,
        ),
 
        TP_fast_assign(
-               __entry->unpacked_lun   = cmd->se_lun->unpacked_lun;
+               __entry->unpacked_lun   = cmd->orig_fe_lun;
                __entry->opcode         = cmd->t_task_cdb[0];
                __entry->data_length    = cmd->data_length;
                __entry->task_attribute = cmd->sam_task_attr;
index 550811712f78c71aad43b4a531b07e1df536b64f..28acbaf4a81ec091a54740c54adfd57ec2f9ad59 100644 (file)
@@ -223,6 +223,8 @@ struct drm_mode_get_connector {
        __u32 connection;
        __u32 mm_width, mm_height; /**< HxW in millimeters */
        __u32 subpixel;
+
+       __u32 pad;
 };
 
 #define DRM_MODE_PROP_PENDING  (1<<0)
index 115add2515aaad2bc5caebd1bf330060fee75ebc..33d2b8fe166dafcd8dd39c5b7a092b8b3d5befa6 100644 (file)
@@ -241,6 +241,8 @@ header-y += media.h
 header-y += mei.h
 header-y += mempolicy.h
 header-y += meye.h
+header-y += mic_common.h
+header-y += mic_ioctl.h
 header-y += mii.h
 header-y += minix_fs.h
 header-y += mman.h
diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h
new file mode 100644 (file)
index 0000000..17e7d95
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC driver.
+ *
+ */
+#ifndef __MIC_COMMON_H_
+#define __MIC_COMMON_H_
+
+#include <linux/virtio_ring.h>
+
+#ifndef __KERNEL__
+#define ALIGN(a, x)    (((a) + (x) - 1) & ~((x) - 1))
+#define __aligned(x)   __attribute__ ((aligned(x)))
+#endif
+
+#define mic_aligned_size(x) ALIGN(sizeof(x), 8)
+
+/**
+ * struct mic_device_desc: Virtio device information shared between the
+ * virtio driver and userspace backend
+ *
+ * @type: Device type: console/network/disk etc.  Type 0/-1 terminates.
+ * @num_vq: Number of virtqueues.
+ * @feature_len: Number of bytes of feature bits.  Multiply by 2: one for
+   host features and one for guest acknowledgements.
+ * @config_len: Number of bytes of the config array after virtqueues.
+ * @status: A status byte, written by the Guest.
+ * @config: Start of the following variable length config.
+ */
+struct mic_device_desc {
+       __s8 type;
+       __u8 num_vq;
+       __u8 feature_len;
+       __u8 config_len;
+       __u8 status;
+       __u64 config[0];
+} __aligned(8);
+
+/**
+ * struct mic_device_ctrl: Per virtio device information in the device page
+ * used internally by the host and card side drivers.
+ *
+ * @vdev: Used for storing MIC vdev information by the guest.
+ * @config_change: Set to 1 by host when a config change is requested.
+ * @vdev_reset: Set to 1 by guest to indicate virtio device has been reset.
+ * @guest_ack: Set to 1 by guest to ack a command.
+ * @host_ack: Set to 1 by host to ack a command.
+ * @used_address_updated: Set to 1 by guest when the used address should be
+ * updated.
+ * @c2h_vdev_db: The doorbell number to be used by guest. Set by host.
+ * @h2c_vdev_db: The doorbell number to be used by host. Set by guest.
+ */
+struct mic_device_ctrl {
+       __u64 vdev;
+       __u8 config_change;
+       __u8 vdev_reset;
+       __u8 guest_ack;
+       __u8 host_ack;
+       __u8 used_address_updated;
+       __s8 c2h_vdev_db;
+       __s8 h2c_vdev_db;
+} __aligned(8);
+
+/**
+ * struct mic_bootparam: Virtio device independent information in device page
+ *
+ * @magic: A magic value used by the card to ensure it can see the host
+ * @c2h_shutdown_db: Card to Host shutdown doorbell set by host
+ * @h2c_shutdown_db: Host to Card shutdown doorbell set by card
+ * @h2c_config_db: Host to Card Virtio config doorbell set by card
+ * @shutdown_status: Card shutdown status set by card
+ * @shutdown_card: Set to 1 by the host when a card shutdown is initiated
+ */
+struct mic_bootparam {
+       __u32 magic;
+       __s8 c2h_shutdown_db;
+       __s8 h2c_shutdown_db;
+       __s8 h2c_config_db;
+       __u8 shutdown_status;
+       __u8 shutdown_card;
+} __aligned(8);
+
+/**
+ * struct mic_device_page: High level representation of the device page
+ *
+ * @bootparam: The bootparam structure is used for sharing information and
+ * status updates between MIC host and card drivers.
+ * @desc: Array of MIC virtio device descriptors.
+ */
+struct mic_device_page {
+       struct mic_bootparam bootparam;
+       struct mic_device_desc desc[0];
+};
+/**
+ * struct mic_vqconfig: This is how we expect the device configuration field
+ * for a virtqueue to be laid out in config space.
+ *
+ * @address: Guest/MIC physical address of the virtio ring
+ * (avail and desc rings)
+ * @used_address: Guest/MIC physical address of the used ring
+ * @num: The number of entries in the virtio_ring
+ */
+struct mic_vqconfig {
+       __u64 address;
+       __u64 used_address;
+       __u16 num;
+} __aligned(8);
+
+/*
+ * The alignment to use between consumer and producer parts of vring.
+ * This is pagesize for historical reasons.
+ */
+#define MIC_VIRTIO_RING_ALIGN          4096
+
+#define MIC_MAX_VRINGS                 4
+#define MIC_VRING_ENTRIES              128
+
+/*
+ * Max vring entries (power of 2) to ensure desc and avail rings
+ * fit in a single page
+ */
+#define MIC_MAX_VRING_ENTRIES          128
+
+/**
+ * Max size of the desc block in bytes: includes:
+ *     - struct mic_device_desc
+ *     - struct mic_vqconfig (num_vq of these)
+ *     - host and guest features
+ *     - virtio device config space
+ */
+#define MIC_MAX_DESC_BLK_SIZE          256
+
+/**
+ * struct _mic_vring_info - Host vring info exposed to userspace backend
+ * for the avail index and magic for the card.
+ *
+ * @avail_idx: host avail idx
+ * @magic: A magic debug cookie.
+ */
+struct _mic_vring_info {
+       __u16 avail_idx;
+       int magic;
+};
+
+/**
+ * struct mic_vring - Vring information.
+ *
+ * @vr: The virtio ring.
+ * @info: Host vring information exposed to the userspace backend for the
+ * avail index and magic for the card.
+ * @va: The va for the buffer allocated for vr and info.
+ * @len: The length of the buffer required for allocating vr and info.
+ */
+struct mic_vring {
+       struct vring vr;
+       struct _mic_vring_info *info;
+       void *va;
+       int len;
+};
+
+#define mic_aligned_desc_size(d) ALIGN(mic_desc_size(d), 8)
+
+#ifndef INTEL_MIC_CARD
+static inline unsigned mic_desc_size(const struct mic_device_desc *desc)
+{
+       return mic_aligned_size(*desc)
+               + desc->num_vq * mic_aligned_size(struct mic_vqconfig)
+               + desc->feature_len * 2
+               + desc->config_len;
+}
+
+static inline struct mic_vqconfig *
+mic_vq_config(const struct mic_device_desc *desc)
+{
+       return (struct mic_vqconfig *)(desc + 1);
+}
+
+static inline __u8 *mic_vq_features(const struct mic_device_desc *desc)
+{
+       return (__u8 *)(mic_vq_config(desc) + desc->num_vq);
+}
+
+static inline __u8 *mic_vq_configspace(const struct mic_device_desc *desc)
+{
+       return mic_vq_features(desc) + desc->feature_len * 2;
+}
+static inline unsigned mic_total_desc_size(struct mic_device_desc *desc)
+{
+       return mic_aligned_desc_size(desc) +
+               mic_aligned_size(struct mic_device_ctrl);
+}
+#endif
+
+/* Device page size */
+#define MIC_DP_SIZE 4096
+
+#define MIC_MAGIC 0xc0ffee00
+
+/**
+ * enum mic_states - MIC states.
+ */
+enum mic_states {
+       MIC_OFFLINE = 0,
+       MIC_ONLINE,
+       MIC_SHUTTING_DOWN,
+       MIC_RESET_FAILED,
+       MIC_SUSPENDING,
+       MIC_SUSPENDED,
+       MIC_LAST
+};
+
+/**
+ * enum mic_status - MIC status reported by card after
+ * a host or card initiated shutdown or a card crash.
+ */
+enum mic_status {
+       MIC_NOP = 0,
+       MIC_CRASHED,
+       MIC_HALTED,
+       MIC_POWER_OFF,
+       MIC_RESTART,
+       MIC_STATUS_LAST
+};
+
+#endif
diff --git a/include/uapi/linux/mic_ioctl.h b/include/uapi/linux/mic_ioctl.h
new file mode 100644 (file)
index 0000000..7fabba5
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel 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.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_IOCTL_H_
+#define _MIC_IOCTL_H_
+
+#include <linux/types.h>
+
+/*
+ * mic_copy - MIC virtio descriptor copy.
+ *
+ * @iov: An array of IOVEC structures containing user space buffers.
+ * @iovcnt: Number of IOVEC structures in iov.
+ * @vr_idx: The vring index.
+ * @update_used: A non zero value results in used index being updated.
+ * @out_len: The aggregate of the total length written to or read from
+ *     the virtio device.
+ */
+struct mic_copy_desc {
+#ifdef __KERNEL__
+       struct iovec __user *iov;
+#else
+       struct iovec *iov;
+#endif
+       int iovcnt;
+       __u8 vr_idx;
+       __u8 update_used;
+       __u32 out_len;
+};
+
+/*
+ * Add a new virtio device
+ * The (struct mic_device_desc *) pointer points to a device page entry
+ *     for the virtio device consisting of:
+ *     - struct mic_device_desc
+ *     - struct mic_vqconfig (num_vq of these)
+ *     - host and guest features
+ *     - virtio device config space
+ * The total size referenced by the pointer should equal the size returned
+ * by desc_size() in mic_common.h
+ */
+#define MIC_VIRTIO_ADD_DEVICE _IOWR('s', 1, struct mic_device_desc *)
+
+/*
+ * Copy the number of entries in the iovec and update the used index
+ * if requested by the user.
+ */
+#define MIC_VIRTIO_COPY_DESC   _IOWR('s', 2, struct mic_copy_desc *)
+
+/*
+ * Notify virtio device of a config change
+ * The (__u8 *) pointer points to config space values for the device
+ * as they should be written into the device page. The total size
+ * referenced by the pointer should equal the config_len field of struct
+ * mic_device_desc.
+ */
+#define MIC_VIRTIO_CONFIG_CHANGE _IOWR('s', 5, __u8 *)
+
+#endif
index 009a655a5d354c51e20fb34cb12ca653e94f9b71..2fc1602e23bb041b9087597370096fcc90e84527 100644 (file)
@@ -456,13 +456,15 @@ struct perf_event_mmap_page {
        /*
         * Control data for the mmap() data buffer.
         *
-        * User-space reading the @data_head value should issue an rmb(), on
-        * SMP capable platforms, after reading this value -- see
-        * perf_event_wakeup().
+        * User-space reading the @data_head value should issue an smp_rmb(),
+        * after reading this value.
         *
         * When the mapping is PROT_WRITE the @data_tail value should be
-        * written by userspace to reflect the last read data. In this case
-        * the kernel will not over-write unread data.
+        * written by userspace to reflect the last read data, after issueing
+        * an smp_mb() to separate the data read from the ->data_tail store.
+        * In this case the kernel will not over-write unread data.
+        *
+        * See perf_output_put_handle() for the data ordering.
         */
        __u64   data_head;              /* head in the data section */
        __u64   data_tail;              /* user-space written tail */
index 0623ec4e728f09550fd8c5e0e464f4a771265bdb..56f121605c998c28e8a173067e94e24ec7d9c653 100644 (file)
@@ -1,5 +1,6 @@
 # UAPI Header export list
 header-y += tc_csum.h
+header-y += tc_defact.h
 header-y += tc_gact.h
 header-y += tc_ipt.h
 header-y += tc_mirred.h
diff --git a/include/uapi/linux/tc_act/tc_defact.h b/include/uapi/linux/tc_act/tc_defact.h
new file mode 100644 (file)
index 0000000..17dddb4
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __LINUX_TC_DEF_H
+#define __LINUX_TC_DEF_H
+
+#include <linux/pkt_cls.h>
+
+struct tc_defact {
+       tc_gen;
+};
+
+enum {
+       TCA_DEF_UNSPEC,
+       TCA_DEF_TM,
+       TCA_DEF_PARMS,
+       TCA_DEF_DATA,
+       __TCA_DEF_MAX
+};
+#define TCA_DEF_MAX (__TCA_DEF_MAX - 1)
+
+#endif
index 0b233c56b0e402ef75f691b5dcf6c6f1cc87a01b..e3ddd86c90a68610a20625729a99cf8effea8f31 100644 (file)
@@ -87,8 +87,10 @@ enum {
        IB_USER_VERBS_CMD_CLOSE_XRCD,
        IB_USER_VERBS_CMD_CREATE_XSRQ,
        IB_USER_VERBS_CMD_OPEN_QP,
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
        IB_USER_VERBS_CMD_CREATE_FLOW = IB_USER_VERBS_CMD_THRESHOLD,
        IB_USER_VERBS_CMD_DESTROY_FLOW
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 };
 
 /*
@@ -126,6 +128,7 @@ struct ib_uverbs_cmd_hdr {
        __u16 out_words;
 };
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 struct ib_uverbs_cmd_hdr_ex {
        __u32 command;
        __u16 in_words;
@@ -134,6 +137,7 @@ struct ib_uverbs_cmd_hdr_ex {
        __u16 provider_out_words;
        __u32 cmd_hdr_reserved;
 };
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 struct ib_uverbs_get_context {
        __u64 response;
@@ -696,6 +700,7 @@ struct ib_uverbs_detach_mcast {
        __u64 driver_data[0];
 };
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 struct ib_kern_eth_filter {
        __u8  dst_mac[6];
        __u8  src_mac[6];
@@ -780,6 +785,7 @@ struct ib_uverbs_destroy_flow  {
        __u32 comp_mask;
        __u32 flow_handle;
 };
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 struct ib_uverbs_create_srq {
        __u64 response;
diff --git a/include/video/exynos_dp.h b/include/video/exynos_dp.h
deleted file mode 100644 (file)
index bd8cabd..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Samsung SoC DP device support
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Author: Jingoo Han <jg1.han@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _EXYNOS_DP_H
-#define _EXYNOS_DP_H
-
-#define DP_TIMEOUT_LOOP_COUNT 100
-#define MAX_CR_LOOP 5
-#define MAX_EQ_LOOP 5
-
-enum link_rate_type {
-       LINK_RATE_1_62GBPS = 0x06,
-       LINK_RATE_2_70GBPS = 0x0a
-};
-
-enum link_lane_count_type {
-       LANE_COUNT1 = 1,
-       LANE_COUNT2 = 2,
-       LANE_COUNT4 = 4
-};
-
-enum link_training_state {
-       START,
-       CLOCK_RECOVERY,
-       EQUALIZER_TRAINING,
-       FINISHED,
-       FAILED
-};
-
-enum voltage_swing_level {
-       VOLTAGE_LEVEL_0,
-       VOLTAGE_LEVEL_1,
-       VOLTAGE_LEVEL_2,
-       VOLTAGE_LEVEL_3,
-};
-
-enum pre_emphasis_level {
-       PRE_EMPHASIS_LEVEL_0,
-       PRE_EMPHASIS_LEVEL_1,
-       PRE_EMPHASIS_LEVEL_2,
-       PRE_EMPHASIS_LEVEL_3,
-};
-
-enum pattern_set {
-       PRBS7,
-       D10_2,
-       TRAINING_PTN1,
-       TRAINING_PTN2,
-       DP_NONE
-};
-
-enum color_space {
-       COLOR_RGB,
-       COLOR_YCBCR422,
-       COLOR_YCBCR444
-};
-
-enum color_depth {
-       COLOR_6,
-       COLOR_8,
-       COLOR_10,
-       COLOR_12
-};
-
-enum color_coefficient {
-       COLOR_YCBCR601,
-       COLOR_YCBCR709
-};
-
-enum dynamic_range {
-       VESA,
-       CEA
-};
-
-enum pll_status {
-       PLL_UNLOCKED,
-       PLL_LOCKED
-};
-
-enum clock_recovery_m_value_type {
-       CALCULATED_M,
-       REGISTER_M
-};
-
-enum video_timing_recognition_type {
-       VIDEO_TIMING_FROM_CAPTURE,
-       VIDEO_TIMING_FROM_REGISTER
-};
-
-enum analog_power_block {
-       AUX_BLOCK,
-       CH0_BLOCK,
-       CH1_BLOCK,
-       CH2_BLOCK,
-       CH3_BLOCK,
-       ANALOG_TOTAL,
-       POWER_ALL
-};
-
-struct video_info {
-       char *name;
-
-       bool h_sync_polarity;
-       bool v_sync_polarity;
-       bool interlaced;
-
-       enum color_space color_space;
-       enum dynamic_range dynamic_range;
-       enum color_coefficient ycbcr_coeff;
-       enum color_depth color_depth;
-
-       enum link_rate_type link_rate;
-       enum link_lane_count_type lane_count;
-};
-
-struct exynos_dp_platdata {
-       struct video_info *video_info;
-
-       void (*phy_init)(void);
-       void (*phy_exit)(void);
-};
-
-#endif /* _EXYNOS_DP_H */
index 89dc88a171af812f1ee01f6b5b64af4e61fc7ca1..6a578f8a1b3e2126c3940d6f4dc2b93db54837bc 100644 (file)
@@ -216,6 +216,7 @@ struct mipi_dsim_config {
  *     automatically.
  * @e_clk_src: select byte clock source.
  * @pd: pointer to MIPI-DSI driver platform data.
+ * @phy: pointer to the MIPI-DSI PHY
  */
 struct mipi_dsim_device {
        struct device                   *dev;
@@ -236,6 +237,7 @@ struct mipi_dsim_device {
        bool                            suspended;
 
        struct mipi_dsim_platform_data  *pd;
+       struct phy                      *phy;
 };
 
 /*
@@ -248,7 +250,6 @@ struct mipi_dsim_device {
  * @enabled: indicate whether mipi controller got enabled or not.
  * @lcd_panel_info: pointer for lcd panel specific structure.
  *     this structure specifies width, height, timing and polarity and so on.
- * @phy_enable: pointer to a callback controlling D-PHY enable/reset
  */
 struct mipi_dsim_platform_data {
        char                            lcd_panel_name[PANEL_NAME_SIZE];
@@ -256,8 +257,6 @@ struct mipi_dsim_platform_data {
        struct mipi_dsim_config         *dsim_config;
        unsigned int                    enabled;
        void                            *lcd_panel_info;
-
-       int (*phy_enable)(struct platform_device *pdev, bool on);
 };
 
 /*
index 130dfece27ac7cc74c40a60c6b8eeceb97963deb..b0e99deb6d05330482c8ec98f1a511f07f9fa5f1 100644 (file)
@@ -62,7 +62,7 @@ static int proc_ipc_dointvec_minmax_orphans(ctl_table *table, int write,
        return err;
 }
 
-static int proc_ipc_callback_dointvec(ctl_table *table, int write,
+static int proc_ipc_callback_dointvec_minmax(ctl_table *table, int write,
        void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table ipc_table;
@@ -72,7 +72,7 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write,
        memcpy(&ipc_table, table, sizeof(ipc_table));
        ipc_table.data = get_ipc(table);
 
-       rc = proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
+       rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
 
        if (write && !rc && lenp_bef == *lenp)
                /*
@@ -152,15 +152,13 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
 #define proc_ipc_dointvec         NULL
 #define proc_ipc_dointvec_minmax   NULL
 #define proc_ipc_dointvec_minmax_orphans   NULL
-#define proc_ipc_callback_dointvec NULL
+#define proc_ipc_callback_dointvec_minmax  NULL
 #define proc_ipcauto_dointvec_minmax NULL
 #endif
 
 static int zero;
 static int one = 1;
-#ifdef CONFIG_CHECKPOINT_RESTORE
 static int int_max = INT_MAX;
-#endif
 
 static struct ctl_table ipc_kern_table[] = {
        {
@@ -198,21 +196,27 @@ static struct ctl_table ipc_kern_table[] = {
                .data           = &init_ipc_ns.msg_ctlmax,
                .maxlen         = sizeof (init_ipc_ns.msg_ctlmax),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec,
+               .proc_handler   = proc_ipc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &int_max,
        },
        {
                .procname       = "msgmni",
                .data           = &init_ipc_ns.msg_ctlmni,
                .maxlen         = sizeof (init_ipc_ns.msg_ctlmni),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_callback_dointvec,
+               .proc_handler   = proc_ipc_callback_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &int_max,
        },
        {
                .procname       =  "msgmnb",
                .data           = &init_ipc_ns.msg_ctlmnb,
                .maxlen         = sizeof (init_ipc_ns.msg_ctlmnb),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec,
+               .proc_handler   = proc_ipc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &int_max,
        },
        {
                .procname       = "sem",
index 2418b6e71a854e187573ec12746481ce863e721a..8bd9cfdc70d7bc020dbc19a05813e2e49443b8e8 100644 (file)
@@ -2039,7 +2039,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
 
                /* @tsk either already exited or can't exit until the end */
                if (tsk->flags & PF_EXITING)
-                       continue;
+                       goto next;
 
                /* as per above, nr_threads may decrease, but not increase. */
                BUG_ON(i >= group_size);
@@ -2047,7 +2047,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
                ent.cgrp = task_cgroup_from_root(tsk, root);
                /* nothing to do if this task is already in the cgroup */
                if (ent.cgrp == cgrp)
-                       continue;
+                       goto next;
                /*
                 * saying GFP_ATOMIC has no effect here because we did prealloc
                 * earlier, but it's good form to communicate our expectations.
@@ -2055,7 +2055,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
                retval = flex_array_put(group, i, &ent, GFP_ATOMIC);
                BUG_ON(retval != 0);
                i++;
-
+       next:
                if (!threadgroup)
                        break;
        } while_each_thread(leader, tsk);
@@ -3188,11 +3188,9 @@ css_next_descendant_post(struct cgroup_subsys_state *pos,
 
        WARN_ON_ONCE(!rcu_read_lock_held());
 
-       /* if first iteration, visit the leftmost descendant */
-       if (!pos) {
-               next = css_leftmost_descendant(root);
-               return next != root ? next : NULL;
-       }
+       /* if first iteration, visit leftmost descendant which may be @root */
+       if (!pos)
+               return css_leftmost_descendant(root);
 
        /* if we visited @root, we're done */
        if (pos == root)
index d49a9d29334cc4d67c24bad9814221a0371a6350..663f43a20f73c02c73fbeba9bcb28deb82be15a2 100644 (file)
@@ -6292,6 +6292,7 @@ type_show(struct device *dev, struct device_attribute *attr, char *page)
 
        return snprintf(page, PAGE_SIZE-1, "%d\n", pmu->type);
 }
+static DEVICE_ATTR_RO(type);
 
 static ssize_t
 perf_event_mux_interval_ms_show(struct device *dev,
@@ -6336,17 +6337,19 @@ perf_event_mux_interval_ms_store(struct device *dev,
 
        return count;
 }
+static DEVICE_ATTR_RW(perf_event_mux_interval_ms);
 
-static struct device_attribute pmu_dev_attrs[] = {
-       __ATTR_RO(type),
-       __ATTR_RW(perf_event_mux_interval_ms),
-       __ATTR_NULL,
+static struct attribute *pmu_dev_attrs[] = {
+       &dev_attr_type.attr,
+       &dev_attr_perf_event_mux_interval_ms.attr,
+       NULL,
 };
+ATTRIBUTE_GROUPS(pmu_dev);
 
 static int pmu_bus_running;
 static struct bus_type pmu_bus = {
        .name           = "event_source",
-       .dev_attrs      = pmu_dev_attrs,
+       .dev_groups     = pmu_dev_groups,
 };
 
 static void pmu_dev_release(struct device *dev)
@@ -6767,6 +6770,10 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
        if (ret)
                return -EFAULT;
 
+       /* disabled for now */
+       if (attr->mmap2)
+               return -EINVAL;
+
        if (attr->__reserved_1)
                return -EINVAL;
 
index cd55144270b5401030f4b5ce5576f97b6b976b63..9c2ddfbf452554902d9ad1c23deba5794d0ed664 100644 (file)
@@ -87,10 +87,31 @@ again:
                goto out;
 
        /*
-        * Publish the known good head. Rely on the full barrier implied
-        * by atomic_dec_and_test() order the rb->head read and this
-        * write.
+        * Since the mmap() consumer (userspace) can run on a different CPU:
+        *
+        *   kernel                             user
+        *
+        *   READ ->data_tail                   READ ->data_head
+        *   smp_mb()   (A)                     smp_rmb()       (C)
+        *   WRITE $data                        READ $data
+        *   smp_wmb()  (B)                     smp_mb()        (D)
+        *   STORE ->data_head                  WRITE ->data_tail
+        *
+        * Where A pairs with D, and B pairs with C.
+        *
+        * I don't think A needs to be a full barrier because we won't in fact
+        * write data until we see the store from userspace. So we simply don't
+        * issue the data WRITE until we observe it. Be conservative for now.
+        *
+        * OTOH, D needs to be a full barrier since it separates the data READ
+        * from the tail WRITE.
+        *
+        * For B a WMB is sufficient since it separates two WRITEs, and for C
+        * an RMB is sufficient since it separates two READs.
+        *
+        * See perf_output_begin().
         */
+       smp_wmb();
        rb->user_page->data_head = head;
 
        /*
@@ -154,9 +175,11 @@ int perf_output_begin(struct perf_output_handle *handle,
                 * Userspace could choose to issue a mb() before updating the
                 * tail pointer. So that all reads will be completed before the
                 * write is issued.
+                *
+                * See perf_output_put_handle().
                 */
                tail = ACCESS_ONCE(rb->user_page->data_tail);
-               smp_rmb();
+               smp_mb();
                offset = head = local_read(&rb->head);
                head += size;
                if (unlikely(!perf_output_space(rb, tail, offset, head)))
index 6d647aedffea494dec6b11d20fa222a05fd49f7f..d24105b1b794e635e93509c04e0ac6edb85cc7f5 100644 (file)
@@ -410,7 +410,7 @@ ww_mutex_set_context_fastpath(struct ww_mutex *lock,
 static __always_inline int __sched
 __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
                    struct lockdep_map *nest_lock, unsigned long ip,
-                   struct ww_acquire_ctx *ww_ctx)
+                   struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)
 {
        struct task_struct *task = current;
        struct mutex_waiter waiter;
@@ -450,7 +450,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
                struct task_struct *owner;
                struct mspin_node  node;
 
-               if (!__builtin_constant_p(ww_ctx == NULL) && ww_ctx->acquired > 0) {
+               if (use_ww_ctx && ww_ctx->acquired > 0) {
                        struct ww_mutex *ww;
 
                        ww = container_of(lock, struct ww_mutex, base);
@@ -480,7 +480,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
                if ((atomic_read(&lock->count) == 1) &&
                    (atomic_cmpxchg(&lock->count, 1, 0) == 1)) {
                        lock_acquired(&lock->dep_map, ip);
-                       if (!__builtin_constant_p(ww_ctx == NULL)) {
+                       if (use_ww_ctx) {
                                struct ww_mutex *ww;
                                ww = container_of(lock, struct ww_mutex, base);
 
@@ -551,7 +551,7 @@ slowpath:
                        goto err;
                }
 
-               if (!__builtin_constant_p(ww_ctx == NULL) && ww_ctx->acquired > 0) {
+               if (use_ww_ctx && ww_ctx->acquired > 0) {
                        ret = __mutex_lock_check_stamp(lock, ww_ctx);
                        if (ret)
                                goto err;
@@ -575,7 +575,7 @@ skip_wait:
        lock_acquired(&lock->dep_map, ip);
        mutex_set_owner(lock);
 
-       if (!__builtin_constant_p(ww_ctx == NULL)) {
+       if (use_ww_ctx) {
                struct ww_mutex *ww = container_of(lock, struct ww_mutex, base);
                struct mutex_waiter *cur;
 
@@ -615,7 +615,7 @@ mutex_lock_nested(struct mutex *lock, unsigned int subclass)
 {
        might_sleep();
        __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE,
-                           subclass, NULL, _RET_IP_, NULL);
+                           subclass, NULL, _RET_IP_, NULL, 0);
 }
 
 EXPORT_SYMBOL_GPL(mutex_lock_nested);
@@ -625,7 +625,7 @@ _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest)
 {
        might_sleep();
        __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE,
-                           0, nest, _RET_IP_, NULL);
+                           0, nest, _RET_IP_, NULL, 0);
 }
 
 EXPORT_SYMBOL_GPL(_mutex_lock_nest_lock);
@@ -635,7 +635,7 @@ mutex_lock_killable_nested(struct mutex *lock, unsigned int subclass)
 {
        might_sleep();
        return __mutex_lock_common(lock, TASK_KILLABLE,
-                                  subclass, NULL, _RET_IP_, NULL);
+                                  subclass, NULL, _RET_IP_, NULL, 0);
 }
 EXPORT_SYMBOL_GPL(mutex_lock_killable_nested);
 
@@ -644,7 +644,7 @@ mutex_lock_interruptible_nested(struct mutex *lock, unsigned int subclass)
 {
        might_sleep();
        return __mutex_lock_common(lock, TASK_INTERRUPTIBLE,
-                                  subclass, NULL, _RET_IP_, NULL);
+                                  subclass, NULL, _RET_IP_, NULL, 0);
 }
 
 EXPORT_SYMBOL_GPL(mutex_lock_interruptible_nested);
@@ -682,7 +682,7 @@ __ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
 
        might_sleep();
        ret =  __mutex_lock_common(&lock->base, TASK_UNINTERRUPTIBLE,
-                                  0, &ctx->dep_map, _RET_IP_, ctx);
+                                  0, &ctx->dep_map, _RET_IP_, ctx, 1);
        if (!ret && ctx->acquired > 1)
                return ww_mutex_deadlock_injection(lock, ctx);
 
@@ -697,7 +697,7 @@ __ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
 
        might_sleep();
        ret = __mutex_lock_common(&lock->base, TASK_INTERRUPTIBLE,
-                                 0, &ctx->dep_map, _RET_IP_, ctx);
+                                 0, &ctx->dep_map, _RET_IP_, ctx, 1);
 
        if (!ret && ctx->acquired > 1)
                return ww_mutex_deadlock_injection(lock, ctx);
@@ -809,28 +809,28 @@ __mutex_lock_slowpath(atomic_t *lock_count)
        struct mutex *lock = container_of(lock_count, struct mutex, count);
 
        __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0,
-                           NULL, _RET_IP_, NULL);
+                           NULL, _RET_IP_, NULL, 0);
 }
 
 static noinline int __sched
 __mutex_lock_killable_slowpath(struct mutex *lock)
 {
        return __mutex_lock_common(lock, TASK_KILLABLE, 0,
-                                  NULL, _RET_IP_, NULL);
+                                  NULL, _RET_IP_, NULL, 0);
 }
 
 static noinline int __sched
 __mutex_lock_interruptible_slowpath(struct mutex *lock)
 {
        return __mutex_lock_common(lock, TASK_INTERRUPTIBLE, 0,
-                                  NULL, _RET_IP_, NULL);
+                                  NULL, _RET_IP_, NULL, 0);
 }
 
 static noinline int __sched
 __ww_mutex_lock_slowpath(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
 {
        return __mutex_lock_common(&lock->base, TASK_UNINTERRUPTIBLE, 0,
-                                  NULL, _RET_IP_, ctx);
+                                  NULL, _RET_IP_, ctx, 1);
 }
 
 static noinline int __sched
@@ -838,7 +838,7 @@ __ww_mutex_lock_interruptible_slowpath(struct ww_mutex *lock,
                                            struct ww_acquire_ctx *ctx)
 {
        return __mutex_lock_common(&lock->base, TASK_INTERRUPTIBLE, 0,
-                                  NULL, _RET_IP_, ctx);
+                                  NULL, _RET_IP_, ctx, 1);
 }
 
 #endif
index c9c759d5a15cbd53831bdf7552dc455a64a40b5b..0121dab83f43d82ddfb620f7423d6c9728f8b01b 100644 (file)
@@ -846,7 +846,7 @@ static int software_resume(void)
        goto Finish;
 }
 
-late_initcall(software_resume);
+late_initcall_sync(software_resume);
 
 
 static const char * const hibernation_modes[] = {
index b2f06f3c6a3ff32ec9f8f2b92bea0c4766ac6f7d..8b80f1bae21a56de33b18ce198d1df5272fbce0f 100644 (file)
@@ -190,7 +190,7 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
 
 #ifdef CONFIG_MAGIC_SYSRQ
 /* Note: sysrq code uses it's own private copy */
-static int __sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
+static int __sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE;
 
 static int sysrq_sysctl_handler(ctl_table *table, int write,
                                void __user *buffer, size_t *lenp,
index 38959c86678987d0306548b73c03b9413c68b321..662c5798a685d77904fcc1080596e4e4bd5d34b3 100644 (file)
@@ -33,29 +33,64 @@ struct ce_unbind {
        int res;
 };
 
-/**
- * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
- * @latch:     value to convert
- * @evt:       pointer to clock event device descriptor
- *
- * Math helper, returns latch value converted to nanoseconds (bound checked)
- */
-u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
+static u64 cev_delta2ns(unsigned long latch, struct clock_event_device *evt,
+                       bool ismax)
 {
        u64 clc = (u64) latch << evt->shift;
+       u64 rnd;
 
        if (unlikely(!evt->mult)) {
                evt->mult = 1;
                WARN_ON(1);
        }
+       rnd = (u64) evt->mult - 1;
+
+       /*
+        * Upper bound sanity check. If the backwards conversion is
+        * not equal latch, we know that the above shift overflowed.
+        */
+       if ((clc >> evt->shift) != (u64)latch)
+               clc = ~0ULL;
+
+       /*
+        * Scaled math oddities:
+        *
+        * For mult <= (1 << shift) we can safely add mult - 1 to
+        * prevent integer rounding loss. So the backwards conversion
+        * from nsec to device ticks will be correct.
+        *
+        * For mult > (1 << shift), i.e. device frequency is > 1GHz we
+        * need to be careful. Adding mult - 1 will result in a value
+        * which when converted back to device ticks can be larger
+        * than latch by up to (mult - 1) >> shift. For the min_delta
+        * calculation we still want to apply this in order to stay
+        * above the minimum device ticks limit. For the upper limit
+        * we would end up with a latch value larger than the upper
+        * limit of the device, so we omit the add to stay below the
+        * device upper boundary.
+        *
+        * Also omit the add if it would overflow the u64 boundary.
+        */
+       if ((~0ULL - clc > rnd) &&
+           (!ismax || evt->mult <= (1U << evt->shift)))
+               clc += rnd;
 
        do_div(clc, evt->mult);
-       if (clc < 1000)
-               clc = 1000;
-       if (clc > KTIME_MAX)
-               clc = KTIME_MAX;
 
-       return clc;
+       /* Deltas less than 1usec are pointless noise */
+       return clc > 1000 ? clc : 1000;
+}
+
+/**
+ * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
+ * @latch:     value to convert
+ * @evt:       pointer to clock event device descriptor
+ *
+ * Math helper, returns latch value converted to nanoseconds (bound checked)
+ */
+u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
+{
+       return cev_delta2ns(latch, evt, false);
 }
 EXPORT_SYMBOL_GPL(clockevent_delta2ns);
 
@@ -380,8 +415,8 @@ void clockevents_config(struct clock_event_device *dev, u32 freq)
                sec = 600;
 
        clockevents_calc_mult_shift(dev, freq, sec);
-       dev->min_delta_ns = clockevent_delta2ns(dev->min_delta_ticks, dev);
-       dev->max_delta_ns = clockevent_delta2ns(dev->max_delta_ticks, dev);
+       dev->min_delta_ns = cev_delta2ns(dev->min_delta_ticks, dev, false);
+       dev->max_delta_ns = cev_delta2ns(dev->max_delta_ticks, dev, true);
 }
 
 /**
index 06344d986eb9dde61f341057f431d3183301f54e..ebef88f61b7d81b1497cc2619e8d84a525b3471d 100644 (file)
@@ -312,6 +312,15 @@ config MAGIC_SYSRQ
          keys are documented in <file:Documentation/sysrq.txt>. Don't say Y
          unless you really know what this hack does.
 
+config MAGIC_SYSRQ_DEFAULT_ENABLE
+       hex "Enable magic SysRq key functions by default"
+       depends on MAGIC_SYSRQ
+       default 0x1
+       help
+         Specifies which SysRq key functions are enabled by default.
+         This may be set to 1 or 0 to enable or disable them all, or
+         to a bitmask as described in Documentation/sysrq.txt.
+
 config DEBUG_KERNEL
        bool "Kernel debugging"
        help
@@ -983,7 +992,7 @@ config DEBUG_KOBJECT
 
 config DEBUG_KOBJECT_RELEASE
        bool "kobject release debugging"
-       depends on DEBUG_KERNEL
+       depends on DEBUG_OBJECTS_TIMERS
        help
          kobjects are reference counted objects.  This means that their
          last reference count put is not predictable, and the kobject can
index 084f7b18d0c0a722e215dce8d14dcda0e6ba78d8..7a1c203083eb59e6e17db1af3e942a1351e756f7 100644 (file)
  */
 
 #include <linux/kobject.h>
+#include <linux/kobj_completion.h>
 #include <linux/string.h>
 #include <linux/export.h>
 #include <linux/stat.h>
 #include <linux/slab.h>
 
+/**
+ * kobject_namespace - return @kobj's namespace tag
+ * @kobj: kobject in question
+ *
+ * Returns namespace tag of @kobj if its parent has namespace ops enabled
+ * and thus @kobj should have a namespace tag associated with it.  Returns
+ * %NULL otherwise.
+ */
+const void *kobject_namespace(struct kobject *kobj)
+{
+       const struct kobj_ns_type_operations *ns_ops = kobj_ns_ops(kobj);
+       const void *ns;
+
+       if (!ns_ops || ns_ops->type == KOBJ_NS_TYPE_NONE)
+               return NULL;
+
+       ns = kobj->ktype->namespace(kobj);
+       WARN_ON(!ns);   /* @kobj in a namespace is required to have !NULL tag */
+       return ns;
+}
+
 /*
  * populate_dir - populate directory with attributes.
  * @kobj: object we're working on.
@@ -46,13 +68,21 @@ static int populate_dir(struct kobject *kobj)
 
 static int create_dir(struct kobject *kobj)
 {
-       int error = 0;
-       error = sysfs_create_dir(kobj);
+       int error;
+
+       error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
        if (!error) {
                error = populate_dir(kobj);
                if (error)
                        sysfs_remove_dir(kobj);
        }
+
+       /*
+        * @kobj->sd may be deleted by an ancestor going away.  Hold an
+        * extra reference so that it stays until @kobj is gone.
+        */
+       sysfs_get(kobj->sd);
+
        return error;
 }
 
@@ -428,7 +458,7 @@ int kobject_rename(struct kobject *kobj, const char *new_name)
                goto out;
        }
 
-       error = sysfs_rename_dir(kobj, new_name);
+       error = sysfs_rename_dir_ns(kobj, new_name, kobject_namespace(kobj));
        if (error)
                goto out;
 
@@ -472,6 +502,7 @@ int kobject_move(struct kobject *kobj, struct kobject *new_parent)
                if (kobj->kset)
                        new_parent = kobject_get(&kobj->kset->kobj);
        }
+
        /* old object path */
        devpath = kobject_get_path(kobj, GFP_KERNEL);
        if (!devpath) {
@@ -486,7 +517,7 @@ int kobject_move(struct kobject *kobj, struct kobject *new_parent)
        sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
        envp[0] = devpath_string;
        envp[1] = NULL;
-       error = sysfs_move_dir(kobj, new_parent);
+       error = sysfs_move_dir_ns(kobj, new_parent, kobject_namespace(kobj));
        if (error)
                goto out;
        old_parent = kobj->parent;
@@ -508,10 +539,15 @@ out:
  */
 void kobject_del(struct kobject *kobj)
 {
+       struct sysfs_dirent *sd;
+
        if (!kobj)
                return;
 
+       sd = kobj->sd;
        sysfs_remove_dir(kobj);
+       sysfs_put(sd);
+
        kobj->state_in_sysfs = 0;
        kobj_kset_leave(kobj);
        kobject_put(kobj->parent);
@@ -726,6 +762,55 @@ const struct sysfs_ops kobj_sysfs_ops = {
        .store  = kobj_attr_store,
 };
 
+/**
+ * kobj_completion_init - initialize a kobj_completion object.
+ * @kc: kobj_completion
+ * @ktype: type of kobject to initialize
+ *
+ * kobj_completion structures can be embedded within structures with different
+ * lifetime rules.  During the release of the enclosing object, we can
+ * wait on the release of the kobject so that we don't free it while it's
+ * still busy.
+ */
+void kobj_completion_init(struct kobj_completion *kc, struct kobj_type *ktype)
+{
+       init_completion(&kc->kc_unregister);
+       kobject_init(&kc->kc_kobj, ktype);
+}
+EXPORT_SYMBOL_GPL(kobj_completion_init);
+
+/**
+ * kobj_completion_release - release a kobj_completion object
+ * @kobj: kobject embedded in kobj_completion
+ *
+ * Used with kobject_release to notify waiters that the kobject has been
+ * released.
+ */
+void kobj_completion_release(struct kobject *kobj)
+{
+       struct kobj_completion *kc = kobj_to_kobj_completion(kobj);
+       complete(&kc->kc_unregister);
+}
+EXPORT_SYMBOL_GPL(kobj_completion_release);
+
+/**
+ * kobj_completion_del_and_wait - release the kobject and wait for it
+ * @kc: kobj_completion object to release
+ *
+ * Delete the kobject from sysfs and drop the reference count.  Then wait
+ * until any other outstanding references are also dropped.  This routine
+ * is only necessary once other references may have been taken on the
+ * kobject.  Typically this happens when the kobject has been published
+ * to sysfs via kobject_add.
+ */
+void kobj_completion_del_and_wait(struct kobj_completion *kc)
+{
+       kobject_del(&kc->kc_kobj);
+       kobject_put(&kc->kc_kobj);
+       wait_for_completion(&kc->kc_unregister);
+}
+EXPORT_SYMBOL_GPL(kobj_completion_del_and_wait);
+
 /**
  * kset_register - initialize and add a kset.
  * @k: kset.
index a685c8a79578b274cb361d442b953507e58d7d63..d16fa295ae1dbd0d43f1303b7796086cd968344c 100644 (file)
@@ -577,7 +577,8 @@ void sg_miter_stop(struct sg_mapping_iter *miter)
                miter->__offset += miter->consumed;
                miter->__remaining -= miter->consumed;
 
-               if (miter->__flags & SG_MITER_TO_SG)
+               if ((miter->__flags & SG_MITER_TO_SG) &&
+                   !PageSlab(miter->page))
                        flush_kernel_dcache_page(miter->page);
 
                if (miter->__flags & SG_MITER_ATOMIC) {
index 610e3df2768a6a5b2ec1e293da4c96dafbbe2d30..cca80d96e509efdbf1fcdfdbdb19e5d671d064d5 100644 (file)
@@ -1278,64 +1278,90 @@ out:
 int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
                                unsigned long addr, pmd_t pmd, pmd_t *pmdp)
 {
+       struct anon_vma *anon_vma = NULL;
        struct page *page;
        unsigned long haddr = addr & HPAGE_PMD_MASK;
+       int page_nid = -1, this_nid = numa_node_id();
        int target_nid;
-       int current_nid = -1;
-       bool migrated;
+       bool page_locked;
+       bool migrated = false;
 
        spin_lock(&mm->page_table_lock);
        if (unlikely(!pmd_same(pmd, *pmdp)))
                goto out_unlock;
 
        page = pmd_page(pmd);
-       get_page(page);
-       current_nid = page_to_nid(page);
+       page_nid = page_to_nid(page);
        count_vm_numa_event(NUMA_HINT_FAULTS);
-       if (current_nid == numa_node_id())
+       if (page_nid == this_nid)
                count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL);
 
+       /*
+        * Acquire the page lock to serialise THP migrations but avoid dropping
+        * page_table_lock if at all possible
+        */
+       page_locked = trylock_page(page);
        target_nid = mpol_misplaced(page, vma, haddr);
        if (target_nid == -1) {
-               put_page(page);
-               goto clear_pmdnuma;
+               /* If the page was locked, there are no parallel migrations */
+               if (page_locked)
+                       goto clear_pmdnuma;
+
+               /*
+                * Otherwise wait for potential migrations and retry. We do
+                * relock and check_same as the page may no longer be mapped.
+                * As the fault is being retried, do not account for it.
+                */
+               spin_unlock(&mm->page_table_lock);
+               wait_on_page_locked(page);
+               page_nid = -1;
+               goto out;
        }
 
-       /* Acquire the page lock to serialise THP migrations */
+       /* Page is misplaced, serialise migrations and parallel THP splits */
+       get_page(page);
        spin_unlock(&mm->page_table_lock);
-       lock_page(page);
+       if (!page_locked)
+               lock_page(page);
+       anon_vma = page_lock_anon_vma_read(page);
 
        /* Confirm the PTE did not while locked */
        spin_lock(&mm->page_table_lock);
        if (unlikely(!pmd_same(pmd, *pmdp))) {
                unlock_page(page);
                put_page(page);
+               page_nid = -1;
                goto out_unlock;
        }
-       spin_unlock(&mm->page_table_lock);
 
-       /* Migrate the THP to the requested node */
+       /*
+        * Migrate the THP to the requested node, returns with page unlocked
+        * and pmd_numa cleared.
+        */
+       spin_unlock(&mm->page_table_lock);
        migrated = migrate_misplaced_transhuge_page(mm, vma,
                                pmdp, pmd, addr, page, target_nid);
-       if (!migrated)
-               goto check_same;
-
-       task_numa_fault(target_nid, HPAGE_PMD_NR, true);
-       return 0;
+       if (migrated)
+               page_nid = target_nid;
 
-check_same:
-       spin_lock(&mm->page_table_lock);
-       if (unlikely(!pmd_same(pmd, *pmdp)))
-               goto out_unlock;
+       goto out;
 clear_pmdnuma:
+       BUG_ON(!PageLocked(page));
        pmd = pmd_mknonnuma(pmd);
        set_pmd_at(mm, haddr, pmdp, pmd);
        VM_BUG_ON(pmd_numa(*pmdp));
        update_mmu_cache_pmd(vma, addr, pmdp);
+       unlock_page(page);
 out_unlock:
        spin_unlock(&mm->page_table_lock);
-       if (current_nid != -1)
-               task_numa_fault(current_nid, HPAGE_PMD_NR, false);
+
+out:
+       if (anon_vma)
+               page_unlock_anon_vma_read(anon_vma);
+
+       if (page_nid != -1)
+               task_numa_fault(page_nid, HPAGE_PMD_NR, migrated);
+
        return 0;
 }
 
index 72467914b85640bb88730cb4a5c54113e531b35a..72f9decb0104f046cef155532200cd91e15fea58 100644 (file)
@@ -81,8 +81,9 @@ restart:
                 * decrement nr_to_walk first so that we don't livelock if we
                 * get stuck on large numbesr of LRU_RETRY items
                 */
-               if (--(*nr_to_walk) == 0)
+               if (!*nr_to_walk)
                        break;
+               --*nr_to_walk;
 
                ret = isolate(item, &nlru->lock, cb_arg);
                switch (ret) {
index 34d3ca9572d6baed85499d099a0050af3b9bdf66..13b9d0f221b8460ae3c361dd51d22ae969e78325 100644 (file)
@@ -54,6 +54,7 @@
 #include <linux/page_cgroup.h>
 #include <linux/cpu.h>
 #include <linux/oom.h>
+#include <linux/lockdep.h>
 #include "internal.h"
 #include <net/sock.h>
 #include <net/ip.h>
@@ -2046,6 +2047,12 @@ static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg,
        return total;
 }
 
+#ifdef CONFIG_LOCKDEP
+static struct lockdep_map memcg_oom_lock_dep_map = {
+       .name = "memcg_oom_lock",
+};
+#endif
+
 static DEFINE_SPINLOCK(memcg_oom_lock);
 
 /*
@@ -2083,7 +2090,8 @@ static bool mem_cgroup_oom_trylock(struct mem_cgroup *memcg)
                        }
                        iter->oom_lock = false;
                }
-       }
+       } else
+               mutex_acquire(&memcg_oom_lock_dep_map, 0, 1, _RET_IP_);
 
        spin_unlock(&memcg_oom_lock);
 
@@ -2095,6 +2103,7 @@ static void mem_cgroup_oom_unlock(struct mem_cgroup *memcg)
        struct mem_cgroup *iter;
 
        spin_lock(&memcg_oom_lock);
+       mutex_release(&memcg_oom_lock_dep_map, 1, _RET_IP_);
        for_each_mem_cgroup_tree(iter, memcg)
                iter->oom_lock = false;
        spin_unlock(&memcg_oom_lock);
@@ -2765,10 +2774,10 @@ done:
        *ptr = memcg;
        return 0;
 nomem:
-       *ptr = NULL;
-       if (gfp_mask & __GFP_NOFAIL)
-               return 0;
-       return -ENOMEM;
+       if (!(gfp_mask & __GFP_NOFAIL)) {
+               *ptr = NULL;
+               return -ENOMEM;
+       }
 bypass:
        *ptr = root_mem_cgroup;
        return -EINTR;
@@ -3773,8 +3782,7 @@ void mem_cgroup_move_account_page_stat(struct mem_cgroup *from,
 {
        /* Update stat data for mem_cgroup */
        preempt_disable();
-       WARN_ON_ONCE(from->stat->count[idx] < nr_pages);
-       __this_cpu_add(from->stat->count[idx], -nr_pages);
+       __this_cpu_sub(from->stat->count[idx], nr_pages);
        __this_cpu_add(to->stat->count[idx], nr_pages);
        preempt_enable();
 }
@@ -4950,31 +4958,18 @@ static void mem_cgroup_reparent_charges(struct mem_cgroup *memcg)
        } while (usage > 0);
 }
 
-/*
- * This mainly exists for tests during the setting of set of use_hierarchy.
- * Since this is the very setting we are changing, the current hierarchy value
- * is meaningless
- */
-static inline bool __memcg_has_children(struct mem_cgroup *memcg)
-{
-       struct cgroup_subsys_state *pos;
-
-       /* bounce at first found */
-       css_for_each_child(pos, &memcg->css)
-               return true;
-       return false;
-}
-
-/*
- * Must be called with memcg_create_mutex held, unless the cgroup is guaranteed
- * to be already dead (as in mem_cgroup_force_empty, for instance).  This is
- * from mem_cgroup_count_children(), in the sense that we don't really care how
- * many children we have; we only need to know if we have any.  It also counts
- * any memcg without hierarchy as infertile.
- */
 static inline bool memcg_has_children(struct mem_cgroup *memcg)
 {
-       return memcg->use_hierarchy && __memcg_has_children(memcg);
+       lockdep_assert_held(&memcg_create_mutex);
+       /*
+        * The lock does not prevent addition or deletion to the list
+        * of children, but it prevents a new child from being
+        * initialized based on this parent in css_online(), so it's
+        * enough to decide whether hierarchically inherited
+        * attributes can still be changed or not.
+        */
+       return memcg->use_hierarchy &&
+               !list_empty(&memcg->css.cgroup->children);
 }
 
 /*
@@ -5054,7 +5049,7 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css,
         */
        if ((!parent_memcg || !parent_memcg->use_hierarchy) &&
                                (val == 1 || val == 0)) {
-               if (!__memcg_has_children(memcg))
+               if (list_empty(&memcg->css.cgroup->children))
                        memcg->use_hierarchy = val;
                else
                        retval = -EBUSY;
index 1311f26497e6a0f776682ed8a7e23b8620a5b9ad..d176154c243f2ffad8cf4b1b39f6f30d17c97733 100644 (file)
@@ -3521,12 +3521,12 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma,
 }
 
 int numa_migrate_prep(struct page *page, struct vm_area_struct *vma,
-                               unsigned long addr, int current_nid)
+                               unsigned long addr, int page_nid)
 {
        get_page(page);
 
        count_vm_numa_event(NUMA_HINT_FAULTS);
-       if (current_nid == numa_node_id())
+       if (page_nid == numa_node_id())
                count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL);
 
        return mpol_misplaced(page, vma, addr);
@@ -3537,7 +3537,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
 {
        struct page *page = NULL;
        spinlock_t *ptl;
-       int current_nid = -1;
+       int page_nid = -1;
        int target_nid;
        bool migrated = false;
 
@@ -3567,15 +3567,10 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
                return 0;
        }
 
-       current_nid = page_to_nid(page);
-       target_nid = numa_migrate_prep(page, vma, addr, current_nid);
+       page_nid = page_to_nid(page);
+       target_nid = numa_migrate_prep(page, vma, addr, page_nid);
        pte_unmap_unlock(ptep, ptl);
        if (target_nid == -1) {
-               /*
-                * Account for the fault against the current node if it not
-                * being replaced regardless of where the page is located.
-                */
-               current_nid = numa_node_id();
                put_page(page);
                goto out;
        }
@@ -3583,11 +3578,11 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
        /* Migrate to the requested node */
        migrated = migrate_misplaced_page(page, target_nid);
        if (migrated)
-               current_nid = target_nid;
+               page_nid = target_nid;
 
 out:
-       if (current_nid != -1)
-               task_numa_fault(current_nid, 1, migrated);
+       if (page_nid != -1)
+               task_numa_fault(page_nid, 1, migrated);
        return 0;
 }
 
@@ -3602,7 +3597,6 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
        unsigned long offset;
        spinlock_t *ptl;
        bool numa = false;
-       int local_nid = numa_node_id();
 
        spin_lock(&mm->page_table_lock);
        pmd = *pmdp;
@@ -3625,9 +3619,10 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
        for (addr = _addr + offset; addr < _addr + PMD_SIZE; pte++, addr += PAGE_SIZE) {
                pte_t pteval = *pte;
                struct page *page;
-               int curr_nid = local_nid;
+               int page_nid = -1;
                int target_nid;
-               bool migrated;
+               bool migrated = false;
+
                if (!pte_present(pteval))
                        continue;
                if (!pte_numa(pteval))
@@ -3649,25 +3644,19 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
                if (unlikely(page_mapcount(page) != 1))
                        continue;
 
-               /*
-                * Note that the NUMA fault is later accounted to either
-                * the node that is currently running or where the page is
-                * migrated to.
-                */
-               curr_nid = local_nid;
-               target_nid = numa_migrate_prep(page, vma, addr,
-                                              page_to_nid(page));
-               if (target_nid == -1) {
+               page_nid = page_to_nid(page);
+               target_nid = numa_migrate_prep(page, vma, addr, page_nid);
+               pte_unmap_unlock(pte, ptl);
+               if (target_nid != -1) {
+                       migrated = migrate_misplaced_page(page, target_nid);
+                       if (migrated)
+                               page_nid = target_nid;
+               } else {
                        put_page(page);
-                       continue;
                }
 
-               /* Migrate to the requested node */
-               pte_unmap_unlock(pte, ptl);
-               migrated = migrate_misplaced_page(page, target_nid);
-               if (migrated)
-                       curr_nid = target_nid;
-               task_numa_fault(curr_nid, 1, migrated);
+               if (page_nid != -1)
+                       task_numa_fault(page_nid, 1, migrated);
 
                pte = pte_offset_map_lock(mm, pmdp, addr, &ptl);
        }
index 7a7325ee1d089696a8073a84d2f748f326124805..c04692774e88bee81f10c2ac85e9cf2752e32237 100644 (file)
@@ -1715,12 +1715,12 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
                unlock_page(new_page);
                put_page(new_page);             /* Free it */
 
-               unlock_page(page);
+               /* Retake the callers reference and putback on LRU */
+               get_page(page);
                putback_lru_page(page);
-
-               count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR);
-               isolated = 0;
-               goto out;
+               mod_zone_page_state(page_zone(page),
+                        NR_ISOLATED_ANON + page_lru, -HPAGE_PMD_NR);
+               goto out_fail;
        }
 
        /*
@@ -1737,9 +1737,9 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
        entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
        entry = pmd_mkhuge(entry);
 
-       page_add_new_anon_rmap(new_page, vma, haddr);
-
+       pmdp_clear_flush(vma, haddr, pmd);
        set_pmd_at(mm, haddr, pmd, entry);
+       page_add_new_anon_rmap(new_page, vma, haddr);
        update_mmu_cache_pmd(vma, address, &entry);
        page_remove_rmap(page);
        /*
@@ -1758,7 +1758,6 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
        count_vm_events(PGMIGRATE_SUCCESS, HPAGE_PMD_NR);
        count_vm_numa_events(NUMA_PAGE_MIGRATE, HPAGE_PMD_NR);
 
-out:
        mod_zone_page_state(page_zone(page),
                        NR_ISOLATED_ANON + page_lru,
                        -HPAGE_PMD_NR);
@@ -1767,6 +1766,10 @@ out:
 out_fail:
        count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR);
 out_dropref:
+       entry = pmd_mknonnuma(entry);
+       set_pmd_at(mm, haddr, pmd, entry);
+       update_mmu_cache_pmd(vma, address, &entry);
+
        unlock_page(page);
        put_page(page);
        return 0;
index a3af058f68e4d9f434337d0dcd6127d5d8c6d039..412ba2b7326a2898d25e4b0a43b3dfd921a389a1 100644 (file)
@@ -148,7 +148,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma,
                                split_huge_page_pmd(vma, addr, pmd);
                        else if (change_huge_pmd(vma, pmd, addr, newprot,
                                                 prot_numa)) {
-                               pages += HPAGE_PMD_NR;
+                               pages++;
                                continue;
                        }
                        /* fall through */
index 5da2cbcfdbb56b0e9f4fe27d6e04137e59dfce3b..2beeabf502c50f1ff3069981bd5b3a84b7463b6f 100644 (file)
@@ -242,7 +242,7 @@ int walk_page_range(unsigned long addr, unsigned long end,
                if (err)
                        break;
                pgd++;
-       } while (addr = next, addr != end);
+       } while (addr = next, addr < end);
 
        return err;
 }
index 309129732285fd610159446bade5ede27c835d1c..c7e634af85165613822074b28ceeca4af7153ae7 100644 (file)
@@ -171,7 +171,7 @@ static size_t vlan_get_size(const struct net_device *dev)
 
        return nla_total_size(2) +      /* IFLA_VLAN_PROTOCOL */
               nla_total_size(2) +      /* IFLA_VLAN_ID */
-              sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */
+              nla_total_size(sizeof(struct ifla_vlan_flags)) + /* IFLA_VLAN_FLAGS */
               vlan_qos_map_size(vlan->nr_ingress_mappings) +
               vlan_qos_map_size(vlan->nr_egress_mappings);
 }
index c72d1bcdcf4906a1a23808cab2a464804a3fcda6..1356af660b5bf05645394a28c91cdbc54e5ad4c2 100644 (file)
@@ -65,6 +65,7 @@ static int __init batadv_init(void)
        batadv_recv_handler_init();
 
        batadv_iv_init();
+       batadv_nc_init();
 
        batadv_event_workqueue = create_singlethread_workqueue("bat_events");
 
@@ -142,7 +143,7 @@ int batadv_mesh_init(struct net_device *soft_iface)
        if (ret < 0)
                goto err;
 
-       ret = batadv_nc_init(bat_priv);
+       ret = batadv_nc_mesh_init(bat_priv);
        if (ret < 0)
                goto err;
 
@@ -167,7 +168,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
        batadv_vis_quit(bat_priv);
 
        batadv_gw_node_purge(bat_priv);
-       batadv_nc_free(bat_priv);
+       batadv_nc_mesh_free(bat_priv);
        batadv_dat_free(bat_priv);
        batadv_bla_free(bat_priv);
 
index a487d46e0aeccdb72ab4ad6e361dd0b4b87a7c05..4ecc0b6bf8ab63a41b57235f6706207bd5da9e99 100644 (file)
@@ -34,6 +34,20 @@ static void batadv_nc_worker(struct work_struct *work);
 static int batadv_nc_recv_coded_packet(struct sk_buff *skb,
                                       struct batadv_hard_iface *recv_if);
 
+/**
+ * batadv_nc_init - one-time initialization for network coding
+ */
+int __init batadv_nc_init(void)
+{
+       int ret;
+
+       /* Register our packet type */
+       ret = batadv_recv_handler_register(BATADV_CODED,
+                                          batadv_nc_recv_coded_packet);
+
+       return ret;
+}
+
 /**
  * batadv_nc_start_timer - initialise the nc periodic worker
  * @bat_priv: the bat priv with all the soft interface information
@@ -45,10 +59,10 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
 }
 
 /**
- * batadv_nc_init - initialise coding hash table and start house keeping
+ * batadv_nc_mesh_init - initialise coding hash table and start house keeping
  * @bat_priv: the bat priv with all the soft interface information
  */
-int batadv_nc_init(struct batadv_priv *bat_priv)
+int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
 {
        bat_priv->nc.timestamp_fwd_flush = jiffies;
        bat_priv->nc.timestamp_sniffed_purge = jiffies;
@@ -70,11 +84,6 @@ int batadv_nc_init(struct batadv_priv *bat_priv)
        batadv_hash_set_lock_class(bat_priv->nc.coding_hash,
                                   &batadv_nc_decoding_hash_lock_class_key);
 
-       /* Register our packet type */
-       if (batadv_recv_handler_register(BATADV_CODED,
-                                        batadv_nc_recv_coded_packet) < 0)
-               goto err;
-
        INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
        batadv_nc_start_timer(bat_priv);
 
@@ -1721,12 +1730,11 @@ free_nc_packet:
 }
 
 /**
- * batadv_nc_free - clean up network coding memory
+ * batadv_nc_mesh_free - clean up network coding memory
  * @bat_priv: the bat priv with all the soft interface information
  */
-void batadv_nc_free(struct batadv_priv *bat_priv)
+void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
 {
-       batadv_recv_handler_unregister(BATADV_CODED);
        cancel_delayed_work_sync(&bat_priv->nc.work);
 
        batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL);
index 85a4ec81ad50bda26449cfdadbcaf28e62391b8b..ddfa618e80bf0202fdf6bc89ffe213b44cbfb28a 100644 (file)
@@ -22,8 +22,9 @@
 
 #ifdef CONFIG_BATMAN_ADV_NC
 
-int batadv_nc_init(struct batadv_priv *bat_priv);
-void batadv_nc_free(struct batadv_priv *bat_priv);
+int batadv_nc_init(void);
+int batadv_nc_mesh_init(struct batadv_priv *bat_priv);
+void batadv_nc_mesh_free(struct batadv_priv *bat_priv);
 void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
                              struct batadv_orig_node *orig_node,
                              struct batadv_orig_node *orig_neigh_node,
@@ -46,12 +47,17 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv);
 
 #else /* ifdef CONFIG_BATMAN_ADV_NC */
 
-static inline int batadv_nc_init(struct batadv_priv *bat_priv)
+static inline int batadv_nc_init(void)
 {
        return 0;
 }
 
-static inline void batadv_nc_free(struct batadv_priv *bat_priv)
+static inline int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
+{
+       return 0;
+}
+
+static inline void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
 {
        return;
 }
index ca04163635da3bb4a090596edb3009afdc6ee033..e6b7fecb3af185e452e99806d5c7962b5fad8f26 100644 (file)
@@ -64,7 +64,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
                        br_flood_deliver(br, skb, false);
                        goto out;
                }
-               if (br_multicast_rcv(br, NULL, skb)) {
+               if (br_multicast_rcv(br, NULL, skb, vid)) {
                        kfree_skb(skb);
                        goto out;
                }
index ffd5874f25920a94c74f5d97ebf4a0e2aa77f48d..33e8f23acddd9ca2c817913753142ce063c1fb9e 100644 (file)
@@ -700,7 +700,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 
                vid = nla_get_u16(tb[NDA_VLAN]);
 
-               if (vid >= VLAN_N_VID) {
+               if (!vid || vid >= VLAN_VID_MASK) {
                        pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
                                vid);
                        return -EINVAL;
@@ -794,7 +794,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 
                vid = nla_get_u16(tb[NDA_VLAN]);
 
-               if (vid >= VLAN_N_VID) {
+               if (!vid || vid >= VLAN_VID_MASK) {
                        pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
                                vid);
                        return -EINVAL;
index a2fd37ec35f7d26a0755f351d4104e9b02f0bb3a..7e73c32e205d10517595395ed2bed91ab48868b9 100644 (file)
@@ -80,7 +80,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
                br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
 
        if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
-           br_multicast_rcv(br, p, skb))
+           br_multicast_rcv(br, p, skb, vid))
                goto drop;
 
        if (p->state == BR_STATE_LEARNING)
index 85a09bb5ca51b864cd671c63746d65dfd531ed79..b7b1914dfa252a3731a26e2bcf2be3afc5171661 100644 (file)
@@ -453,7 +453,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
                call_rcu_bh(&p->rcu, br_multicast_free_pg);
                err = 0;
 
-               if (!mp->ports && !mp->mglist && mp->timer_armed &&
+               if (!mp->ports && !mp->mglist &&
                    netif_running(br->dev))
                        mod_timer(&mp->timer, jiffies);
                break;
index d1c5786306784a7353b845cad60e2d39abec0f72..686284ff3d6a1cabd0f16251d19346e11a810256 100644 (file)
@@ -272,7 +272,7 @@ static void br_multicast_del_pg(struct net_bridge *br,
                del_timer(&p->timer);
                call_rcu_bh(&p->rcu, br_multicast_free_pg);
 
-               if (!mp->ports && !mp->mglist && mp->timer_armed &&
+               if (!mp->ports && !mp->mglist &&
                    netif_running(br->dev))
                        mod_timer(&mp->timer, jiffies);
 
@@ -620,7 +620,6 @@ rehash:
 
        mp->br = br;
        mp->addr = *group;
-
        setup_timer(&mp->timer, br_multicast_group_expired,
                    (unsigned long)mp);
 
@@ -660,6 +659,7 @@ static int br_multicast_add_group(struct net_bridge *br,
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       unsigned long now = jiffies;
        int err;
 
        spin_lock(&br->multicast_lock);
@@ -674,6 +674,7 @@ static int br_multicast_add_group(struct net_bridge *br,
 
        if (!port) {
                mp->mglist = true;
+               mod_timer(&mp->timer, now + br->multicast_membership_interval);
                goto out;
        }
 
@@ -681,7 +682,7 @@ static int br_multicast_add_group(struct net_bridge *br,
             (p = mlock_dereference(*pp, br)) != NULL;
             pp = &p->next) {
                if (p->port == port)
-                       goto out;
+                       goto found;
                if ((unsigned long)p->port < (unsigned long)port)
                        break;
        }
@@ -692,6 +693,8 @@ static int br_multicast_add_group(struct net_bridge *br,
        rcu_assign_pointer(*pp, p);
        br_mdb_notify(br->dev, port, group, RTM_NEWMDB);
 
+found:
+       mod_timer(&p->timer, now + br->multicast_membership_interval);
 out:
        err = 0;
 
@@ -944,7 +947,8 @@ void br_multicast_disable_port(struct net_bridge_port *port)
 
 static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
                                         struct net_bridge_port *port,
-                                        struct sk_buff *skb)
+                                        struct sk_buff *skb,
+                                        u16 vid)
 {
        struct igmpv3_report *ih;
        struct igmpv3_grec *grec;
@@ -954,12 +958,10 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
        int type;
        int err = 0;
        __be32 group;
-       u16 vid = 0;
 
        if (!pskb_may_pull(skb, sizeof(*ih)))
                return -EINVAL;
 
-       br_vlan_get_tag(skb, &vid);
        ih = igmpv3_report_hdr(skb);
        num = ntohs(ih->ngrec);
        len = sizeof(*ih);
@@ -1002,7 +1004,8 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
 #if IS_ENABLED(CONFIG_IPV6)
 static int br_ip6_multicast_mld2_report(struct net_bridge *br,
                                        struct net_bridge_port *port,
-                                       struct sk_buff *skb)
+                                       struct sk_buff *skb,
+                                       u16 vid)
 {
        struct icmp6hdr *icmp6h;
        struct mld2_grec *grec;
@@ -1010,12 +1013,10 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
        int len;
        int num;
        int err = 0;
-       u16 vid = 0;
 
        if (!pskb_may_pull(skb, sizeof(*icmp6h)))
                return -EINVAL;
 
-       br_vlan_get_tag(skb, &vid);
        icmp6h = icmp6_hdr(skb);
        num = ntohs(icmp6h->icmp6_dataun.un_data16[1]);
        len = sizeof(*icmp6h);
@@ -1138,7 +1139,8 @@ static void br_multicast_query_received(struct net_bridge *br,
 
 static int br_ip4_multicast_query(struct net_bridge *br,
                                  struct net_bridge_port *port,
-                                 struct sk_buff *skb)
+                                 struct sk_buff *skb,
+                                 u16 vid)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct igmphdr *ih = igmp_hdr(skb);
@@ -1150,7 +1152,6 @@ static int br_ip4_multicast_query(struct net_bridge *br,
        unsigned long now = jiffies;
        __be32 group;
        int err = 0;
-       u16 vid = 0;
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) ||
@@ -1186,14 +1187,10 @@ static int br_ip4_multicast_query(struct net_bridge *br,
        if (!group)
                goto out;
 
-       br_vlan_get_tag(skb, &vid);
        mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
                goto out;
 
-       mod_timer(&mp->timer, now + br->multicast_membership_interval);
-       mp->timer_armed = true;
-
        max_delay *= br->multicast_last_member_count;
 
        if (mp->mglist &&
@@ -1219,7 +1216,8 @@ out:
 #if IS_ENABLED(CONFIG_IPV6)
 static int br_ip6_multicast_query(struct net_bridge *br,
                                  struct net_bridge_port *port,
-                                 struct sk_buff *skb)
+                                 struct sk_buff *skb,
+                                 u16 vid)
 {
        const struct ipv6hdr *ip6h = ipv6_hdr(skb);
        struct mld_msg *mld;
@@ -1231,7 +1229,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
        unsigned long now = jiffies;
        const struct in6_addr *group = NULL;
        int err = 0;
-       u16 vid = 0;
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) ||
@@ -1265,14 +1262,10 @@ static int br_ip6_multicast_query(struct net_bridge *br,
        if (!group)
                goto out;
 
-       br_vlan_get_tag(skb, &vid);
        mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
                goto out;
 
-       mod_timer(&mp->timer, now + br->multicast_membership_interval);
-       mp->timer_armed = true;
-
        max_delay *= br->multicast_last_member_count;
        if (mp->mglist &&
            (timer_pending(&mp->timer) ?
@@ -1358,7 +1351,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
                        call_rcu_bh(&p->rcu, br_multicast_free_pg);
                        br_mdb_notify(br->dev, port, group, RTM_DELMDB);
 
-                       if (!mp->ports && !mp->mglist && mp->timer_armed &&
+                       if (!mp->ports && !mp->mglist &&
                            netif_running(br->dev))
                                mod_timer(&mp->timer, jiffies);
                }
@@ -1370,12 +1363,30 @@ static void br_multicast_leave_group(struct net_bridge *br,
                     br->multicast_last_member_interval;
 
        if (!port) {
-               if (mp->mglist && mp->timer_armed &&
+               if (mp->mglist &&
                    (timer_pending(&mp->timer) ?
                     time_after(mp->timer.expires, time) :
                     try_to_del_timer_sync(&mp->timer) >= 0)) {
                        mod_timer(&mp->timer, time);
                }
+
+               goto out;
+       }
+
+       for (p = mlock_dereference(mp->ports, br);
+            p != NULL;
+            p = mlock_dereference(p->next, br)) {
+               if (p->port != port)
+                       continue;
+
+               if (!hlist_unhashed(&p->mglist) &&
+                   (timer_pending(&p->timer) ?
+                    time_after(p->timer.expires, time) :
+                    try_to_del_timer_sync(&p->timer) >= 0)) {
+                       mod_timer(&p->timer, time);
+               }
+
+               break;
        }
 out:
        spin_unlock(&br->multicast_lock);
@@ -1424,7 +1435,8 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
 
 static int br_multicast_ipv4_rcv(struct net_bridge *br,
                                 struct net_bridge_port *port,
-                                struct sk_buff *skb)
+                                struct sk_buff *skb,
+                                u16 vid)
 {
        struct sk_buff *skb2 = skb;
        const struct iphdr *iph;
@@ -1432,7 +1444,6 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
        unsigned int len;
        unsigned int offset;
        int err;
-       u16 vid = 0;
 
        /* We treat OOM as packet loss for now. */
        if (!pskb_may_pull(skb, sizeof(*iph)))
@@ -1493,7 +1504,6 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
 
        err = 0;
 
-       br_vlan_get_tag(skb2, &vid);
        BR_INPUT_SKB_CB(skb)->igmp = 1;
        ih = igmp_hdr(skb2);
 
@@ -1504,10 +1514,10 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
                err = br_ip4_multicast_add_group(br, port, ih->group, vid);
                break;
        case IGMPV3_HOST_MEMBERSHIP_REPORT:
-               err = br_ip4_multicast_igmp3_report(br, port, skb2);
+               err = br_ip4_multicast_igmp3_report(br, port, skb2, vid);
                break;
        case IGMP_HOST_MEMBERSHIP_QUERY:
-               err = br_ip4_multicast_query(br, port, skb2);
+               err = br_ip4_multicast_query(br, port, skb2, vid);
                break;
        case IGMP_HOST_LEAVE_MESSAGE:
                br_ip4_multicast_leave_group(br, port, ih->group, vid);
@@ -1525,7 +1535,8 @@ err_out:
 #if IS_ENABLED(CONFIG_IPV6)
 static int br_multicast_ipv6_rcv(struct net_bridge *br,
                                 struct net_bridge_port *port,
-                                struct sk_buff *skb)
+                                struct sk_buff *skb,
+                                u16 vid)
 {
        struct sk_buff *skb2;
        const struct ipv6hdr *ip6h;
@@ -1535,7 +1546,6 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
        unsigned int len;
        int offset;
        int err;
-       u16 vid = 0;
 
        if (!pskb_may_pull(skb, sizeof(*ip6h)))
                return -EINVAL;
@@ -1625,7 +1635,6 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
 
        err = 0;
 
-       br_vlan_get_tag(skb, &vid);
        BR_INPUT_SKB_CB(skb)->igmp = 1;
 
        switch (icmp6_type) {
@@ -1642,10 +1651,10 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
                break;
            }
        case ICMPV6_MLD2_REPORT:
-               err = br_ip6_multicast_mld2_report(br, port, skb2);
+               err = br_ip6_multicast_mld2_report(br, port, skb2, vid);
                break;
        case ICMPV6_MGM_QUERY:
-               err = br_ip6_multicast_query(br, port, skb2);
+               err = br_ip6_multicast_query(br, port, skb2, vid);
                break;
        case ICMPV6_MGM_REDUCTION:
            {
@@ -1666,7 +1675,7 @@ out:
 #endif
 
 int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
-                    struct sk_buff *skb)
+                    struct sk_buff *skb, u16 vid)
 {
        BR_INPUT_SKB_CB(skb)->igmp = 0;
        BR_INPUT_SKB_CB(skb)->mrouters_only = 0;
@@ -1676,10 +1685,10 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
 
        switch (skb->protocol) {
        case htons(ETH_P_IP):
-               return br_multicast_ipv4_rcv(br, port, skb);
+               return br_multicast_ipv4_rcv(br, port, skb, vid);
 #if IS_ENABLED(CONFIG_IPV6)
        case htons(ETH_P_IPV6):
-               return br_multicast_ipv6_rcv(br, port, skb);
+               return br_multicast_ipv6_rcv(br, port, skb, vid);
 #endif
        }
 
@@ -1798,7 +1807,6 @@ void br_multicast_stop(struct net_bridge *br)
                hlist_for_each_entry_safe(mp, n, &mdb->mhash[i],
                                          hlist[ver]) {
                        del_timer(&mp->timer);
-                       mp->timer_armed = false;
                        call_rcu_bh(&mp->rcu, br_multicast_free_group);
                }
        }
index e74ddc1c29a8be20f1a093fcc8e27b11bca6f03e..f75d92e4f96b33cec6fd46fc37151224f86ed018 100644 (file)
@@ -243,7 +243,7 @@ static int br_afspec(struct net_bridge *br,
 
                vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]);
 
-               if (vinfo->vid >= VLAN_N_VID)
+               if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
                        return -EINVAL;
 
                switch (cmd) {
index efb57d91156975b3b43a2afdc588128a2f6e1f79..2e8244efb262d54362bd36a12b9c3000c4388260 100644 (file)
@@ -126,7 +126,6 @@ struct net_bridge_mdb_entry
        struct timer_list               timer;
        struct br_ip                    addr;
        bool                            mglist;
-       bool                            timer_armed;
 };
 
 struct net_bridge_mdb_htable
@@ -452,7 +451,8 @@ extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __us
 extern unsigned int br_mdb_rehash_seq;
 extern int br_multicast_rcv(struct net_bridge *br,
                            struct net_bridge_port *port,
-                           struct sk_buff *skb);
+                           struct sk_buff *skb,
+                           u16 vid);
 extern struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
                                               struct sk_buff *skb, u16 vid);
 extern void br_multicast_add_port(struct net_bridge_port *port);
@@ -523,7 +523,8 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br,
 #else
 static inline int br_multicast_rcv(struct net_bridge *br,
                                   struct net_bridge_port *port,
-                                  struct sk_buff *skb)
+                                  struct sk_buff *skb,
+                                  u16 vid)
 {
        return 0;
 }
@@ -643,9 +644,7 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
         * vid wasn't set
         */
        smp_rmb();
-       return (v->pvid & VLAN_TAG_PRESENT) ?
-                       (v->pvid & ~VLAN_TAG_PRESENT) :
-                       VLAN_N_VID;
+       return v->pvid ?: VLAN_N_VID;
 }
 
 #else
index 108084a0467160e30eb001cd2dbb326fdc4dadd3..656a6f3e40de1b13b9ea7a89373da5d5615e5bb2 100644 (file)
@@ -134,7 +134,7 @@ static void br_stp_start(struct net_bridge *br)
 
        if (br->bridge_forward_delay < BR_MIN_FORWARD_DELAY)
                __br_set_forward_delay(br, BR_MIN_FORWARD_DELAY);
-       else if (br->bridge_forward_delay < BR_MAX_FORWARD_DELAY)
+       else if (br->bridge_forward_delay > BR_MAX_FORWARD_DELAY)
                __br_set_forward_delay(br, BR_MAX_FORWARD_DELAY);
 
        if (r == 0) {
index 9a9ffe7e4019741d75456e3b9afdba21c44785b3..53f0990eab58e08a3da9160a27ee0834bd0f0272 100644 (file)
@@ -45,37 +45,34 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
                return 0;
        }
 
-       if (vid) {
-               if (v->port_idx) {
-                       p = v->parent.port;
-                       br = p->br;
-                       dev = p->dev;
-               } else {
-                       br = v->parent.br;
-                       dev = br->dev;
-               }
-               ops = dev->netdev_ops;
-
-               if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
-                       /* Add VLAN to the device filter if it is supported.
-                        * Stricly speaking, this is not necessary now, since
-                        * devices are made promiscuous by the bridge, but if
-                        * that ever changes this code will allow tagged
-                        * traffic to enter the bridge.
-                        */
-                       err = ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q),
-                                                      vid);
-                       if (err)
-                               return err;
-               }
-
-               err = br_fdb_insert(br, p, dev->dev_addr, vid);
-               if (err) {
-                       br_err(br, "failed insert local address into bridge "
-                              "forwarding table\n");
-                       goto out_filt;
-               }
+       if (v->port_idx) {
+               p = v->parent.port;
+               br = p->br;
+               dev = p->dev;
+       } else {
+               br = v->parent.br;
+               dev = br->dev;
+       }
+       ops = dev->netdev_ops;
+
+       if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
+               /* Add VLAN to the device filter if it is supported.
+                * Stricly speaking, this is not necessary now, since
+                * devices are made promiscuous by the bridge, but if
+                * that ever changes this code will allow tagged
+                * traffic to enter the bridge.
+                */
+               err = ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q),
+                                              vid);
+               if (err)
+                       return err;
+       }
 
+       err = br_fdb_insert(br, p, dev->dev_addr, vid);
+       if (err) {
+               br_err(br, "failed insert local address into bridge "
+                      "forwarding table\n");
+               goto out_filt;
        }
 
        set_bit(vid, v->vlan_bitmap);
@@ -98,7 +95,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
        __vlan_delete_pvid(v, vid);
        clear_bit(vid, v->untagged_bitmap);
 
-       if (v->port_idx && vid) {
+       if (v->port_idx) {
                struct net_device *dev = v->parent.port->dev;
                const struct net_device_ops *ops = dev->netdev_ops;
 
@@ -192,6 +189,8 @@ out:
 bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
                        struct sk_buff *skb, u16 *vid)
 {
+       int err;
+
        /* If VLAN filtering is disabled on the bridge, all packets are
         * permitted.
         */
@@ -204,20 +203,32 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        if (!v)
                return false;
 
-       if (br_vlan_get_tag(skb, vid)) {
+       err = br_vlan_get_tag(skb, vid);
+       if (!*vid) {
                u16 pvid = br_get_pvid(v);
 
-               /* Frame did not have a tag.  See if pvid is set
-                * on this port.  That tells us which vlan untagged
-                * traffic belongs to.
+               /* Frame had a tag with VID 0 or did not have a tag.
+                * See if pvid is set on this port.  That tells us which
+                * vlan untagged or priority-tagged traffic belongs to.
                 */
                if (pvid == VLAN_N_VID)
                        return false;
 
-               /* PVID is set on this port.  Any untagged ingress
-                * frame is considered to belong to this vlan.
+               /* PVID is set on this port.  Any untagged or priority-tagged
+                * ingress frame is considered to belong to this vlan.
                 */
-               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
+               *vid = pvid;
+               if (likely(err))
+                       /* Untagged Frame. */
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
+               else
+                       /* Priority-tagged Frame.
+                        * At this point, We know that skb->vlan_tci had
+                        * VLAN_TAG_PRESENT bit and its VID field was 0x000.
+                        * We update only VID field and preserve PCP field.
+                        */
+                       skb->vlan_tci |= pvid;
+
                return true;
        }
 
@@ -248,7 +259,9 @@ bool br_allowed_egress(struct net_bridge *br,
        return false;
 }
 
-/* Must be protected by RTNL */
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
 int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
 {
        struct net_port_vlans *pv = NULL;
@@ -278,7 +291,9 @@ out:
        return err;
 }
 
-/* Must be protected by RTNL */
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
 int br_vlan_delete(struct net_bridge *br, u16 vid)
 {
        struct net_port_vlans *pv;
@@ -289,14 +304,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
        if (!pv)
                return -EINVAL;
 
-       if (vid) {
-               /* If the VID !=0 remove fdb for this vid. VID 0 is special
-                * in that it's the default and is always there in the fdb.
-                */
-               spin_lock_bh(&br->hash_lock);
-               fdb_delete_by_addr(br, br->dev->dev_addr, vid);
-               spin_unlock_bh(&br->hash_lock);
-       }
+       spin_lock_bh(&br->hash_lock);
+       fdb_delete_by_addr(br, br->dev->dev_addr, vid);
+       spin_unlock_bh(&br->hash_lock);
 
        __vlan_del(pv, vid);
        return 0;
@@ -329,7 +339,9 @@ unlock:
        return 0;
 }
 
-/* Must be protected by RTNL */
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
 int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 {
        struct net_port_vlans *pv = NULL;
@@ -363,7 +375,9 @@ clean_up:
        return err;
 }
 
-/* Must be protected by RTNL */
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
 int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
 {
        struct net_port_vlans *pv;
@@ -374,14 +388,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
        if (!pv)
                return -EINVAL;
 
-       if (vid) {
-               /* If the VID !=0 remove fdb for this vid. VID 0 is special
-                * in that it's the default and is always there in the fdb.
-                */
-               spin_lock_bh(&port->br->hash_lock);
-               fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
-               spin_unlock_bh(&port->br->hash_lock);
-       }
+       spin_lock_bh(&port->br->hash_lock);
+       fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
+       spin_unlock_bh(&port->br->hash_lock);
 
        return __vlan_del(pv, vid);
 }
index 518093802d1d640f3642e997b1481279fad9b68b..7c470c371e14f82a9734d3da4d0bb0a177169251 100644 (file)
@@ -181,6 +181,7 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,
        ub->qlen++;
 
        pm = nlmsg_data(nlh);
+       memset(pm, 0, sizeof(*pm));
 
        /* Fill in the ulog data */
        pm->version = EBT_ULOG_VERSION;
@@ -193,8 +194,6 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,
        pm->hook = hooknr;
        if (uloginfo->prefix != NULL)
                strcpy(pm->prefix, uloginfo->prefix);
-       else
-               *(pm->prefix) = '\0';
 
        if (in) {
                strcpy(pm->physindev, in->name);
@@ -204,16 +203,14 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,
                        strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name);
                else
                        strcpy(pm->indev, in->name);
-       } else
-               pm->indev[0] = pm->physindev[0] = '\0';
+       }
 
        if (out) {
                /* If out exists, then out is a bridge port */
                strcpy(pm->physoutdev, out->name);
                /* rcu_read_lock()ed by nf_hook_slow */
                strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name);
-       } else
-               pm->outdev[0] = pm->physoutdev[0] = '\0';
+       }
 
        if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)
                BUG();
index f0a1ba6c8086acc65a87e519fca48853a3bd091e..89032580bd1d8aa5ef29d1395661bdd0bcfe95f1 100644 (file)
@@ -71,6 +71,8 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg)
            __get_user(kmsg->msg_controllen, &umsg->msg_controllen) ||
            __get_user(kmsg->msg_flags, &umsg->msg_flags))
                return -EFAULT;
+       if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+               return -EINVAL;
        kmsg->msg_name = compat_ptr(tmp1);
        kmsg->msg_iov = compat_ptr(tmp2);
        kmsg->msg_control = compat_ptr(tmp3);
index 65f829cfd928b3bda157b0848e13edfa5b118bae..3430b1ed12e5f93b14ca1f377f848fa29a81be00 100644 (file)
@@ -1917,7 +1917,8 @@ static struct xps_map *expand_xps_map(struct xps_map *map,
        return new_map;
 }
 
-int netif_set_xps_queue(struct net_device *dev, struct cpumask *mask, u16 index)
+int netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask,
+                       u16 index)
 {
        struct xps_dev_maps *dev_maps, *new_dev_maps = NULL;
        struct xps_map *map, *new_map;
index 6438f29ff26650b240be40d7d80953dc28f13cb0..01b780856db29e5a2a7ebbc36aa7062340b28bb8 100644 (file)
@@ -644,7 +644,6 @@ void sk_filter_release_rcu(struct rcu_head *rcu)
        struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
 
        bpf_jit_free(fp);
-       kfree(fp);
 }
 EXPORT_SYMBOL(sk_filter_release_rcu);
 
@@ -683,7 +682,7 @@ int sk_unattached_filter_create(struct sk_filter **pfp,
        if (fprog->filter == NULL)
                return -EINVAL;
 
-       fp = kmalloc(fsize + sizeof(*fp), GFP_KERNEL);
+       fp = kmalloc(sk_filter_size(fprog->len), GFP_KERNEL);
        if (!fp)
                return -ENOMEM;
        memcpy(fp->insns, fprog->filter, fsize);
@@ -723,6 +722,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 {
        struct sk_filter *fp, *old_fp;
        unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
+       unsigned int sk_fsize = sk_filter_size(fprog->len);
        int err;
 
        if (sock_flag(sk, SOCK_FILTER_LOCKED))
@@ -732,11 +732,11 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
        if (fprog->filter == NULL)
                return -EINVAL;
 
-       fp = sock_kmalloc(sk, fsize+sizeof(*fp), GFP_KERNEL);
+       fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL);
        if (!fp)
                return -ENOMEM;
        if (copy_from_user(fp->insns, fprog->filter, fsize)) {
-               sock_kfree_s(sk, fp, fsize+sizeof(*fp));
+               sock_kfree_s(sk, fp, sk_fsize);
                return -EFAULT;
        }
 
index 8d7d0dd72db211e23b5bcffd16f7600841841598..143b6fdb9647dae2d9c9f65d1a72e56bce1a1cc5 100644 (file)
@@ -40,7 +40,7 @@ again:
                struct iphdr _iph;
 ip:
                iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
-               if (!iph)
+               if (!iph || iph->ihl < 5)
                        return false;
 
                if (ip_is_fragment(iph))
index d954b56b4e47976b3b003c41cb7b39758d5d044d..325dee863e466dc74cc7fa4fd26abb56a2fae85c 100644 (file)
@@ -1344,17 +1344,19 @@ int netdev_register_kobject(struct net_device *net)
        return error;
 }
 
-int netdev_class_create_file(struct class_attribute *class_attr)
+int netdev_class_create_file_ns(struct class_attribute *class_attr,
+                               const void *ns)
 {
-       return class_create_file(&net_class, class_attr);
+       return class_create_file_ns(&net_class, class_attr, ns);
 }
-EXPORT_SYMBOL(netdev_class_create_file);
+EXPORT_SYMBOL(netdev_class_create_file_ns);
 
-void netdev_class_remove_file(struct class_attribute *class_attr)
+void netdev_class_remove_file_ns(struct class_attribute *class_attr,
+                                const void *ns)
 {
-       class_remove_file(&net_class, class_attr);
+       class_remove_file_ns(&net_class, class_attr, ns);
 }
-EXPORT_SYMBOL(netdev_class_remove_file);
+EXPORT_SYMBOL(netdev_class_remove_file_ns);
 
 int netdev_kobject_init(void)
 {
index fc75c9e461b8d366d5c29417735432dc573f08ae..8f971990677cd48026f651b2dba01d01190bfff6 100644 (file)
@@ -636,8 +636,9 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo
 
                        netpoll_send_skb(np, send_skb);
 
-                       /* If there are several rx_hooks for the same address,
-                          we're fine by sending a single reply */
+                       /* If there are several rx_skb_hooks for the same
+                        * address we're fine by sending a single reply
+                        */
                        break;
                }
                spin_unlock_irqrestore(&npinfo->rx_lock, flags);
@@ -719,8 +720,9 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo
 
                        netpoll_send_skb(np, send_skb);
 
-                       /* If there are several rx_hooks for the same address,
-                          we're fine by sending a single reply */
+                       /* If there are several rx_skb_hooks for the same
+                        * address, we're fine by sending a single reply
+                        */
                        break;
                }
                spin_unlock_irqrestore(&npinfo->rx_lock, flags);
@@ -756,11 +758,12 @@ static bool pkt_is_ns(struct sk_buff *skb)
 
 int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
 {
-       int proto, len, ulen;
-       int hits = 0;
+       int proto, len, ulen, data_len;
+       int hits = 0, offset;
        const struct iphdr *iph;
        struct udphdr *uh;
        struct netpoll *np, *tmp;
+       uint16_t source;
 
        if (list_empty(&npinfo->rx_np))
                goto out;
@@ -820,7 +823,10 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
 
                len -= iph->ihl*4;
                uh = (struct udphdr *)(((char *)iph) + iph->ihl*4);
+               offset = (unsigned char *)(uh + 1) - skb->data;
                ulen = ntohs(uh->len);
+               data_len = skb->len - offset;
+               source = ntohs(uh->source);
 
                if (ulen != len)
                        goto out;
@@ -834,9 +840,7 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
                        if (np->local_port && np->local_port != ntohs(uh->dest))
                                continue;
 
-                       np->rx_hook(np, ntohs(uh->source),
-                                      (char *)(uh+1),
-                                      ulen - sizeof(struct udphdr));
+                       np->rx_skb_hook(np, source, skb, offset, data_len);
                        hits++;
                }
        } else {
@@ -859,7 +863,10 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
                if (!pskb_may_pull(skb, sizeof(struct udphdr)))
                        goto out;
                uh = udp_hdr(skb);
+               offset = (unsigned char *)(uh + 1) - skb->data;
                ulen = ntohs(uh->len);
+               data_len = skb->len - offset;
+               source = ntohs(uh->source);
                if (ulen != skb->len)
                        goto out;
                if (udp6_csum_init(skb, uh, IPPROTO_UDP))
@@ -872,9 +879,7 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
                        if (np->local_port && np->local_port != ntohs(uh->dest))
                                continue;
 
-                       np->rx_hook(np, ntohs(uh->source),
-                                      (char *)(uh+1),
-                                      ulen - sizeof(struct udphdr));
+                       np->rx_skb_hook(np, source, skb, offset, data_len);
                        hits++;
                }
 #endif
@@ -1062,7 +1067,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
 
        npinfo->netpoll = np;
 
-       if (np->rx_hook) {
+       if (np->rx_skb_hook) {
                spin_lock_irqsave(&npinfo->rx_lock, flags);
                npinfo->rx_flags |= NETPOLL_RX_ENABLED;
                list_add_tail(&np->rx, &npinfo->rx_np);
index 3f1ec1586ae174d9bea18de2bcada31c3c88a5bb..8d9d05edd2eb1e66024311c62c6b7679c52bdc7d 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <net/secure_seq.h>
 
+#if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET)
 #define NET_SECRET_SIZE (MD5_MESSAGE_BYTES / 4)
 
 static u32 net_secret[NET_SECRET_SIZE] ____cacheline_aligned;
@@ -29,6 +30,7 @@ static void net_secret_init(void)
                cmpxchg(&net_secret[--i], 0, tmp);
        }
 }
+#endif
 
 #ifdef CONFIG_INET
 static u32 seq_scale(u32 seq)
index 5b6beba494a350cb28adfc7724487f1a13e6c011..0b39e7ae43837e602ff473b641675fbf22b83b5c 100644 (file)
@@ -2319,6 +2319,7 @@ void sock_init_data(struct socket *sock, struct sock *sk)
        sk->sk_ll_usec          =       sysctl_net_busy_read;
 #endif
 
+       sk->sk_pacing_rate = ~0U;
        /*
         * Before updating sk_refcnt, we must commit prior changes to memory
         * (Documentation/RCU/rculist_nulls.txt for details)
index c85e71e0c7ffc640bd9592ce52b03dbde6cad926..ff41b4d60d302e18a1ebcf1bdfc950970fdd3aae 100644 (file)
@@ -1372,6 +1372,8 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
        real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
        if (!real_dev)
                return -ENODEV;
+       if (real_dev->type != ARPHRD_IEEE802154)
+               return -EINVAL;
 
        lowpan_dev_info(dev)->real_dev = real_dev;
        lowpan_dev_info(dev)->fragment_tag = 0;
@@ -1386,6 +1388,9 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
 
        entry->ldev = dev;
 
+       /* Set the lowpan harware address to the wpan hardware address. */
+       memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
+
        mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
        INIT_LIST_HEAD(&entry->list);
        list_add_tail(&entry->list, &lowpan_devices);
index 7bd8983dbfcf308e61dc55bb9491b3dd6866fa35..96da9c77decad927aa7a29ea816efa2a3013db37 100644 (file)
@@ -287,7 +287,7 @@ begintw:
                        if (unlikely(!INET_TW_MATCH(sk, net, acookie,
                                                    saddr, daddr, ports,
                                                    dif))) {
-                               sock_put(sk);
+                               inet_twsk_put(inet_twsk(sk));
                                goto begintw;
                        }
                        goto out;
index a04d872c54f919c7133e7830773301cdf070f3ed..3982eabf61e126060fc7c5b48042bea2f0417135 100644 (file)
@@ -772,15 +772,20 @@ static inline int ip_ufo_append_data(struct sock *sk,
                /* initialize protocol header pointer */
                skb->transport_header = skb->network_header + fragheaderlen;
 
-               skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum = 0;
 
-               /* specify the length of each IP datagram fragment */
-               skb_shinfo(skb)->gso_size = maxfraglen - fragheaderlen;
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+
                __skb_queue_tail(queue, skb);
+       } else if (skb_is_gso(skb)) {
+               goto append;
        }
 
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       /* specify the length of each IP datagram fragment */
+       skb_shinfo(skb)->gso_size = maxfraglen - fragheaderlen;
+       skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+
+append:
        return skb_append_datato_frags(sk, skb, getfrag, from,
                                       (length - transhdrlen));
 }
index e805e7b3030e3dad2f8fd83d140f0bb7f100d69c..6e87f853d03334567906c8782250049c568e4280 100644 (file)
@@ -125,8 +125,17 @@ static int vti_rcv(struct sk_buff *skb)
                                  iph->saddr, iph->daddr, 0);
        if (tunnel != NULL) {
                struct pcpu_tstats *tstats;
+               u32 oldmark = skb->mark;
+               int ret;
 
-               if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+
+               /* temporarily mark the skb with the tunnel o_key, to
+                * only match policies with this mark.
+                */
+               skb->mark = be32_to_cpu(tunnel->parms.o_key);
+               ret = xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb);
+               skb->mark = oldmark;
+               if (!ret)
                        return -1;
 
                tstats = this_cpu_ptr(tunnel->dev->tstats);
@@ -135,7 +144,6 @@ static int vti_rcv(struct sk_buff *skb)
                tstats->rx_bytes += skb->len;
                u64_stats_update_end(&tstats->syncp);
 
-               skb->mark = 0;
                secpath_reset(skb);
                skb->dev = tunnel->dev;
                return 1;
@@ -167,7 +175,7 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 
        memset(&fl4, 0, sizeof(fl4));
        flowi4_init_output(&fl4, tunnel->parms.link,
-                          be32_to_cpu(tunnel->parms.i_key), RT_TOS(tos),
+                          be32_to_cpu(tunnel->parms.o_key), RT_TOS(tos),
                           RT_SCOPE_UNIVERSE,
                           IPPROTO_IPIP, 0,
                           dst, tiph->saddr, 0, 0);
index 85a4f21aac1ad117f740d40da7123a663fdfefa8..59da7cde072447c2331422659adb7dadad4f3fae 100644 (file)
@@ -271,6 +271,11 @@ unsigned int arpt_do_table(struct sk_buff *skb,
        local_bh_disable();
        addend = xt_write_recseq_begin();
        private = table->private;
+       /*
+        * Ensure we load private-> members after we've fetched the base
+        * pointer.
+        */
+       smp_read_barrier_depends();
        table_base = private->entries[smp_processor_id()];
 
        e = get_entry(table_base, private->hook_entry[hook]);
index d23118d95ff9291401396953d094391db0e2e44e..718dfbd30cbe09560c1d545525b446fb5695f6af 100644 (file)
@@ -327,6 +327,11 @@ ipt_do_table(struct sk_buff *skb,
        addend = xt_write_recseq_begin();
        private = table->private;
        cpu        = smp_processor_id();
+       /*
+        * Ensure we load private-> members after we've fetched the base
+        * pointer.
+        */
+       smp_read_barrier_depends();
        table_base = private->entries[cpu];
        jumpstack  = (struct ipt_entry **)private->jumpstack[cpu];
        stackptr   = per_cpu_ptr(private->stackptr, cpu);
index cbc22158af490589833a4918f3610050a60d5be4..9cb993cd224bf702ca48382a0656163837433c54 100644 (file)
@@ -220,6 +220,7 @@ static void ipt_ulog_packet(struct net *net,
        ub->qlen++;
 
        pm = nlmsg_data(nlh);
+       memset(pm, 0, sizeof(*pm));
 
        /* We might not have a timestamp, get one */
        if (skb->tstamp.tv64 == 0)
@@ -238,8 +239,6 @@ static void ipt_ulog_packet(struct net *net,
        }
        else if (loginfo->prefix[0] != '\0')
                strncpy(pm->prefix, loginfo->prefix, sizeof(pm->prefix));
-       else
-               *(pm->prefix) = '\0';
 
        if (in && in->hard_header_len > 0 &&
            skb->mac_header != skb->network_header &&
@@ -251,13 +250,9 @@ static void ipt_ulog_packet(struct net *net,
 
        if (in)
                strncpy(pm->indev_name, in->name, sizeof(pm->indev_name));
-       else
-               pm->indev_name[0] = '\0';
 
        if (out)
                strncpy(pm->outdev_name, out->name, sizeof(pm->outdev_name));
-       else
-               pm->outdev_name[0] = '\0';
 
        /* copy_len <= skb->len, so can't fail. */
        if (skb_copy_bits(skb, 0, pm->payload, copy_len) < 0)
index 727f4365bcdff3acdb415fe75fd878a3bc5050af..6011615e810d397ce8ffa5daeca58ce6921e85f0 100644 (file)
@@ -2072,7 +2072,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
                                                              RT_SCOPE_LINK);
                        goto make_route;
                }
-               if (fl4->saddr) {
+               if (!fl4->saddr) {
                        if (ipv4_is_multicast(fl4->daddr))
                                fl4->saddr = inet_select_addr(dev_out, 0,
                                                              fl4->flowi4_scope);
index 25a89eaa669de9240036abc04f1385eb168f995e..068c8fb0d1585ec8823c0310b0a369fb76a559c0 100644 (file)
@@ -1284,7 +1284,10 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
                tp->lost_cnt_hint -= tcp_skb_pcount(prev);
        }
 
-       TCP_SKB_CB(skb)->tcp_flags |= TCP_SKB_CB(prev)->tcp_flags;
+       TCP_SKB_CB(prev)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
+       if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
+               TCP_SKB_CB(prev)->end_seq++;
+
        if (skb == tcp_highest_sack(sk))
                tcp_advance_highest_sack(sk, skb);
 
@@ -2853,7 +2856,8 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
         * left edge of the send window.
         * See draft-ietf-tcplw-high-performance-00, section 3.3.
         */
-       if (seq_rtt < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)
+       if (seq_rtt < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
+           flag & FLAG_ACKED)
                seq_rtt = tcp_time_stamp - tp->rx_opt.rcv_tsecr;
 
        if (seq_rtt < 0)
@@ -2868,14 +2872,19 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
 }
 
 /* Compute time elapsed between (last) SYNACK and the ACK completing 3WHS. */
-static void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req)
+static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        s32 seq_rtt = -1;
 
-       if (tp->lsndtime && !tp->total_retrans)
-               seq_rtt = tcp_time_stamp - tp->lsndtime;
-       tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt, -1);
+       if (synack_stamp && !tp->total_retrans)
+               seq_rtt = tcp_time_stamp - synack_stamp;
+
+       /* If the ACK acks both the SYNACK and the (Fast Open'd) data packets
+        * sent in SYN_RECV, SYNACK RTT is the smooth RTT computed in tcp_ack()
+        */
+       if (!tp->srtt)
+               tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt, -1);
 }
 
 static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
@@ -2978,6 +2987,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
        s32 seq_rtt = -1;
        s32 ca_seq_rtt = -1;
        ktime_t last_ackt = net_invalid_timestamp();
+       bool rtt_update;
 
        while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {
                struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
@@ -3054,14 +3064,13 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
        if (skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED))
                flag |= FLAG_SACK_RENEGING;
 
-       if (tcp_ack_update_rtt(sk, flag, seq_rtt, sack_rtt) ||
-           (flag & FLAG_ACKED))
-               tcp_rearm_rto(sk);
+       rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt, sack_rtt);
 
        if (flag & FLAG_ACKED) {
                const struct tcp_congestion_ops *ca_ops
                        = inet_csk(sk)->icsk_ca_ops;
 
+               tcp_rearm_rto(sk);
                if (unlikely(icsk->icsk_mtup.probe_size &&
                             !after(tp->mtu_probe.probe_seq_end, tp->snd_una))) {
                        tcp_mtup_probe_success(sk);
@@ -3100,6 +3109,13 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
 
                        ca_ops->pkts_acked(sk, pkts_acked, rtt_us);
                }
+       } else if (skb && rtt_update && sack_rtt >= 0 &&
+                  sack_rtt > (s32)(now - TCP_SKB_CB(skb)->when)) {
+               /* Do not re-arm RTO if the sack RTT is measured from data sent
+                * after when the head was last (re)transmitted. Otherwise the
+                * timeout may continue to extend in loss recovery.
+                */
+               tcp_rearm_rto(sk);
        }
 
 #if FASTRETRANS_DEBUG > 0
@@ -3288,7 +3304,7 @@ static void tcp_process_tlp_ack(struct sock *sk, u32 ack, int flag)
                        tcp_init_cwnd_reduction(sk, true);
                        tcp_set_ca_state(sk, TCP_CA_CWR);
                        tcp_end_cwnd_reduction(sk);
-                       tcp_set_ca_state(sk, TCP_CA_Open);
+                       tcp_try_keep_open(sk);
                        NET_INC_STATS_BH(sock_net(sk),
                                         LINUX_MIB_TCPLOSSPROBERECOVERY);
                }
@@ -5584,6 +5600,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
        struct request_sock *req;
        int queued = 0;
        bool acceptable;
+       u32 synack_stamp;
 
        tp->rx_opt.saw_tstamp = 0;
 
@@ -5666,9 +5683,11 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                 * so release it.
                 */
                if (req) {
+                       synack_stamp = tcp_rsk(req)->snt_synack;
                        tp->total_retrans = req->num_retrans;
                        reqsk_fastopen_remove(sk, req, false);
                } else {
+                       synack_stamp = tp->lsndtime;
                        /* Make sure socket is routed, for correct metrics. */
                        icsk->icsk_af_ops->rebuild_header(sk);
                        tcp_init_congestion_control(sk);
@@ -5691,7 +5710,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
                tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale;
                tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
-               tcp_synack_rtt_meas(sk, req);
+               tcp_synack_rtt_meas(sk, synack_stamp);
 
                if (tp->rx_opt.tstamp_ok)
                        tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
@@ -5709,6 +5728,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                } else
                        tcp_init_metrics(sk);
 
+               tcp_update_pacing_rate(sk);
+
                /* Prevent spurious tcp_cwnd_restart() on first data packet */
                tp->lsndtime = tcp_time_stamp;
 
index 3a7525e6c08633dad9424bc1a9cb13bf26dfec1e..533c58a5cfb7a9cf9f4f2b1842533916b20b39da 100644 (file)
@@ -18,6 +18,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                                netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
+       unsigned int sum_truesize = 0;
        struct tcphdr *th;
        unsigned int thlen;
        unsigned int seq;
@@ -102,13 +103,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                if (copy_destructor) {
                        skb->destructor = gso_skb->destructor;
                        skb->sk = gso_skb->sk;
-                       /* {tcp|sock}_wfree() use exact truesize accounting :
-                        * sum(skb->truesize) MUST be exactly be gso_skb->truesize
-                        * So we account mss bytes of 'true size' for each segment.
-                        * The last segment will contain the remaining.
-                        */
-                       skb->truesize = mss;
-                       gso_skb->truesize -= mss;
+                       sum_truesize += skb->truesize;
                }
                skb = skb->next;
                th = tcp_hdr(skb);
@@ -125,7 +120,9 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
        if (copy_destructor) {
                swap(gso_skb->sk, skb->sk);
                swap(gso_skb->destructor, skb->destructor);
-               swap(gso_skb->truesize, skb->truesize);
+               sum_truesize += skb->truesize;
+               atomic_add(sum_truesize - gso_skb->truesize,
+                          &skb->sk->sk_wmem_alloc);
        }
 
        delta = htonl(oldlen + (skb_tail_pointer(skb) -
index e6bb8256e59f3738280a022f250f6fefd621cb38..d46f2143305c2632e7703f2677310dd177d67208 100644 (file)
@@ -637,6 +637,8 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
        unsigned int size = 0;
        unsigned int eff_sacks;
 
+       opts->options = 0;
+
 #ifdef CONFIG_TCP_MD5SIG
        *md5 = tp->af_specific->md5_lookup(sk, sk);
        if (unlikely(*md5)) {
@@ -984,8 +986,10 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
 static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
                                 unsigned int mss_now)
 {
-       if (skb->len <= mss_now || !sk_can_gso(sk) ||
-           skb->ip_summed == CHECKSUM_NONE) {
+       /* Make sure we own this skb before messing gso_size/gso_segs */
+       WARN_ON_ONCE(skb_cloned(skb));
+
+       if (skb->len <= mss_now || skb->ip_summed == CHECKSUM_NONE) {
                /* Avoid the costly divide in the normal
                 * non-TSO case.
                 */
@@ -1065,9 +1069,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
        if (nsize < 0)
                nsize = 0;
 
-       if (skb_cloned(skb) &&
-           skb_is_nonlinear(skb) &&
-           pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+       if (skb_unclone(skb, GFP_ATOMIC))
                return -ENOMEM;
 
        /* Get a new skb... force flag on. */
@@ -2342,6 +2344,8 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                int oldpcount = tcp_skb_pcount(skb);
 
                if (unlikely(oldpcount > 1)) {
+                       if (skb_unclone(skb, GFP_ATOMIC))
+                               return -ENOMEM;
                        tcp_init_tso_segs(sk, skb, cur_mss);
                        tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb));
                }
index 9a459be24af762b42e2d667618f7149c055e5ef6..e1a63930a96789b7df67a8b9914cd3bb69fb1cd9 100644 (file)
@@ -104,9 +104,14 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
        const struct iphdr *iph = ip_hdr(skb);
        u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
        struct flowi4 *fl4 = &fl->u.ip4;
+       int oif = 0;
+
+       if (skb_dst(skb))
+               oif = skb_dst(skb)->dev->ifindex;
 
        memset(fl4, 0, sizeof(struct flowi4));
        fl4->flowi4_mark = skb->mark;
+       fl4->flowi4_oif = reverse ? skb->skb_iif : oif;
 
        if (!ip_is_fragment(iph)) {
                switch (iph->protocol) {
@@ -235,7 +240,7 @@ static struct dst_ops xfrm4_dst_ops = {
        .destroy =              xfrm4_dst_destroy,
        .ifdown =               xfrm4_dst_ifdown,
        .local_out =            __ip_local_out,
-       .gc_thresh =            1024,
+       .gc_thresh =            32768,
 };
 
 static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
index 73784c3d4642e09f0f9f92d1f699769ec08258d9..82e1da3a40b915e65c2ecf15662415511cc91286 100644 (file)
@@ -618,8 +618,7 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);
        struct xfrm_state *x;
 
-       if (type != ICMPV6_DEST_UNREACH &&
-           type != ICMPV6_PKT_TOOBIG &&
+       if (type != ICMPV6_PKT_TOOBIG &&
            type != NDISC_REDIRECT)
                return;
 
index d3618a78fcac4b1f6e606a904196de79235833b1..e67e63f9858d7feae9e6fba4de667edb8f81875d 100644 (file)
@@ -436,8 +436,7 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset);
        struct xfrm_state *x;
 
-       if (type != ICMPV6_DEST_UNREACH &&
-           type != ICMPV6_PKT_TOOBIG &&
+       if (type != ICMPV6_PKT_TOOBIG &&
            type != NDISC_REDIRECT)
                return;
 
index 32b4a1675d826d8ce50e36dbf314456075e1660e..066640e0ba8e3b5f4759cabcfc5bbe125ef2df20 100644 (file)
@@ -116,7 +116,7 @@ begintw:
                        }
                        if (unlikely(!INET6_TW_MATCH(sk, net, saddr, daddr,
                                                     ports, dif))) {
-                               sock_put(sk);
+                               inet_twsk_put(inet_twsk(sk));
                                goto begintw;
                        }
                        goto out;
index 7bb5446b9d73c16a7f4096f2705ce60f997c7c2a..bf4a9a084de5aa8f733318276d6e84cc37d5e249 100644 (file)
@@ -976,6 +976,7 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
                if (t->parms.o_flags&GRE_SEQ)
                        addend += 4;
        }
+       t->hlen = addend;
 
        if (p->flags & IP6_TNL_F_CAP_XMIT) {
                int strict = (ipv6_addr_type(&p->raddr) &
@@ -1002,8 +1003,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
                }
                ip6_rt_put(rt);
        }
-
-       t->hlen = addend;
 }
 
 static int ip6gre_tnl_change(struct ip6_tnl *t,
@@ -1173,9 +1172,8 @@ done:
 
 static int ip6gre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 {
-       struct ip6_tnl *tunnel = netdev_priv(dev);
        if (new_mtu < 68 ||
-           new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen)
+           new_mtu > 0xFFF8 - dev->hard_header_len)
                return -EINVAL;
        dev->mtu = new_mtu;
        return 0;
index a54c45ce4a48f0d3a65f6c54ac77bb73a6a41280..91fb4e8212f52d434e6d072103f70dbb8d219326 100644 (file)
@@ -105,7 +105,7 @@ static int ip6_finish_output2(struct sk_buff *skb)
        }
 
        rcu_read_lock_bh();
-       nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
+       nexthop = rt6_nexthop((struct rt6_info *)dst);
        neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
        if (unlikely(!neigh))
                neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
@@ -874,7 +874,7 @@ static int ip6_dst_lookup_tail(struct sock *sk,
         */
        rt = (struct rt6_info *) *dst;
        rcu_read_lock_bh();
-       n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt, &fl6->daddr));
+       n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt));
        err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0;
        rcu_read_unlock_bh();
 
@@ -1008,6 +1008,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
 
 {
        struct sk_buff *skb;
+       struct frag_hdr fhdr;
        int err;
 
        /* There is support for UDP large send offload by network
@@ -1015,8 +1016,6 @@ static inline int ip6_ufo_append_data(struct sock *sk,
         * udp datagram
         */
        if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
-               struct frag_hdr fhdr;
-
                skb = sock_alloc_send_skb(sk,
                        hh_len + fragheaderlen + transhdrlen + 20,
                        (flags & MSG_DONTWAIT), &err);
@@ -1036,20 +1035,24 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                skb->transport_header = skb->network_header + fragheaderlen;
 
                skb->protocol = htons(ETH_P_IPV6);
-               skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum = 0;
 
-               /* Specify the length of each IPv6 datagram fragment.
-                * It has to be a multiple of 8.
-                */
-               skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
-                                            sizeof(struct frag_hdr)) & ~7;
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
-               ipv6_select_ident(&fhdr, rt);
-               skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
                __skb_queue_tail(&sk->sk_write_queue, skb);
+       } else if (skb_is_gso(skb)) {
+               goto append;
        }
 
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       /* Specify the length of each IPv6 datagram fragment.
+        * It has to be a multiple of 8.
+        */
+       skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
+                                    sizeof(struct frag_hdr)) & ~7;
+       skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+       ipv6_select_ident(&fhdr, rt);
+       skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
+
+append:
        return skb_append_datato_frags(sk, skb, getfrag, from,
                                       (length - transhdrlen));
 }
index a791552e042212d866b2cd96c35e2f0b5d289ab5..583b77e2f69be1d1499da479e2f8da1435d22a97 100644 (file)
@@ -1430,9 +1430,17 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 static int
 ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
 {
-       if (new_mtu < IPV6_MIN_MTU) {
-               return -EINVAL;
+       struct ip6_tnl *tnl = netdev_priv(dev);
+
+       if (tnl->parms.proto == IPPROTO_IPIP) {
+               if (new_mtu < 68)
+                       return -EINVAL;
+       } else {
+               if (new_mtu < IPV6_MIN_MTU)
+                       return -EINVAL;
        }
+       if (new_mtu > 0xFFF8 - dev->hard_header_len)
+               return -EINVAL;
        dev->mtu = new_mtu;
        return 0;
 }
index 5636a912074acb8ecf445d9d6df753ff8e3eba95..ce507d9e1c900d3990e6025b37087309f850eaca 100644 (file)
@@ -64,8 +64,7 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                (struct ip_comp_hdr *)(skb->data + offset);
        struct xfrm_state *x;
 
-       if (type != ICMPV6_DEST_UNREACH &&
-           type != ICMPV6_PKT_TOOBIG &&
+       if (type != ICMPV6_PKT_TOOBIG &&
            type != NDISC_REDIRECT)
                return;
 
index 44400c216dc6361e4607fe9af3e2999639766a11..710238f58aa93c0319cf90adb0c203c35f8bc7e4 100644 (file)
@@ -349,6 +349,11 @@ ip6t_do_table(struct sk_buff *skb,
        local_bh_disable();
        addend = xt_write_recseq_begin();
        private = table->private;
+       /*
+        * Ensure we load private-> members after we've fetched the base
+        * pointer.
+        */
+       smp_read_barrier_depends();
        cpu        = smp_processor_id();
        table_base = private->entries[cpu];
        jumpstack  = (struct ip6t_entry **)private->jumpstack[cpu];
index c979dd96d82a838534ae0105dbfb9e665ad410e6..04e17b3309fbe91af3aaf8caad4a6c32af1eab41 100644 (file)
@@ -476,6 +476,24 @@ out:
 }
 
 #ifdef CONFIG_IPV6_ROUTER_PREF
+struct __rt6_probe_work {
+       struct work_struct work;
+       struct in6_addr target;
+       struct net_device *dev;
+};
+
+static void rt6_probe_deferred(struct work_struct *w)
+{
+       struct in6_addr mcaddr;
+       struct __rt6_probe_work *work =
+               container_of(w, struct __rt6_probe_work, work);
+
+       addrconf_addr_solict_mult(&work->target, &mcaddr);
+       ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL);
+       dev_put(work->dev);
+       kfree(w);
+}
+
 static void rt6_probe(struct rt6_info *rt)
 {
        struct neighbour *neigh;
@@ -499,17 +517,23 @@ static void rt6_probe(struct rt6_info *rt)
 
        if (!neigh ||
            time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
-               struct in6_addr mcaddr;
-               struct in6_addr *target;
+               struct __rt6_probe_work *work;
+
+               work = kmalloc(sizeof(*work), GFP_ATOMIC);
 
-               if (neigh) {
+               if (neigh && work)
                        neigh->updated = jiffies;
+
+               if (neigh)
                        write_unlock(&neigh->lock);
-               }
 
-               target = (struct in6_addr *)&rt->rt6i_gateway;
-               addrconf_addr_solict_mult(target, &mcaddr);
-               ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
+               if (work) {
+                       INIT_WORK(&work->work, rt6_probe_deferred);
+                       work->target = rt->rt6i_gateway;
+                       dev_hold(rt->dst.dev);
+                       work->dev = rt->dst.dev;
+                       schedule_work(&work->work);
+               }
        } else {
 out:
                write_unlock(&neigh->lock);
@@ -851,7 +875,6 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
                        if (ort->rt6i_dst.plen != 128 &&
                            ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
                                rt->rt6i_flags |= RTF_ANYCAST;
-                       rt->rt6i_gateway = *daddr;
                }
 
                rt->rt6i_flags |= RTF_CACHE;
@@ -1064,10 +1087,13 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
        if (rt->rt6i_genid != rt_genid_ipv6(dev_net(rt->dst.dev)))
                return NULL;
 
-       if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
-               return dst;
+       if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
+               return NULL;
 
-       return NULL;
+       if (rt6_check_expired(rt))
+               return NULL;
+
+       return dst;
 }
 
 static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
@@ -1338,6 +1364,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
        rt->dst.flags |= DST_HOST;
        rt->dst.output  = ip6_output;
        atomic_set(&rt->dst.__refcnt, 1);
+       rt->rt6i_gateway  = fl6->daddr;
        rt->rt6i_dst.addr = fl6->daddr;
        rt->rt6i_dst.plen = 128;
        rt->rt6i_idev     = idev;
@@ -1873,7 +1900,10 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
                        in6_dev_hold(rt->rt6i_idev);
                rt->dst.lastuse = jiffies;
 
-               rt->rt6i_gateway = ort->rt6i_gateway;
+               if (ort->rt6i_flags & RTF_GATEWAY)
+                       rt->rt6i_gateway = ort->rt6i_gateway;
+               else
+                       rt->rt6i_gateway = *dest;
                rt->rt6i_flags = ort->rt6i_flags;
                if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
                    (RTF_DEFAULT | RTF_ADDRCONF))
@@ -2160,6 +2190,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
        else
                rt->rt6i_flags |= RTF_LOCAL;
 
+       rt->rt6i_gateway  = *addr;
        rt->rt6i_dst.addr = *addr;
        rt->rt6i_dst.plen = 128;
        rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
index 72b7eaaf3ca0e3e6b7cad3edea8aa69e71cbe147..18786098fd412dd5fa95a9fc096195f387b53fa0 100644 (file)
@@ -1225,9 +1225,6 @@ do_udp_sendmsg:
        if (tclass < 0)
                tclass = np->tclass;
 
-       if (dontfrag < 0)
-               dontfrag = np->dontfrag;
-
        if (msg->msg_flags&MSG_CONFIRM)
                goto do_confirm;
 back_from_confirm:
@@ -1246,6 +1243,8 @@ back_from_confirm:
        up->pending = AF_INET6;
 
 do_append_data:
+       if (dontfrag < 0)
+               dontfrag = np->dontfrag;
        up->len += ulen;
        getfrag  =  is_udplite ?  udplite_getfrag : ip_generic_getfrag;
        err = ip6_append_data(sk, getfrag, msg->msg_iov, ulen,
index 23ed03d786c8376cc59f9fa2cf577ee01a4c2c2d..5f8e128c512d664080251bf6958c89021a1110c1 100644 (file)
@@ -135,9 +135,14 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
        struct ipv6_opt_hdr *exthdr;
        const unsigned char *nh = skb_network_header(skb);
        u8 nexthdr = nh[IP6CB(skb)->nhoff];
+       int oif = 0;
+
+       if (skb_dst(skb))
+               oif = skb_dst(skb)->dev->ifindex;
 
        memset(fl6, 0, sizeof(struct flowi6));
        fl6->flowi6_mark = skb->mark;
+       fl6->flowi6_oif = reverse ? skb->skb_iif : oif;
 
        fl6->daddr = reverse ? hdr->saddr : hdr->daddr;
        fl6->saddr = reverse ? hdr->daddr : hdr->saddr;
@@ -284,7 +289,7 @@ static struct dst_ops xfrm6_dst_ops = {
        .destroy =              xfrm6_dst_destroy,
        .ifdown =               xfrm6_dst_ifdown,
        .local_out =            __ip6_local_out,
-       .gc_thresh =            1024,
+       .gc_thresh =            32768,
 };
 
 static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
index 9d585370c5b4d6a1e60728df4dad6c79ca196ee3..911ef03bf8fbf1716672fb503f39de7fe13746d9 100644 (file)
@@ -1098,7 +1098,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
 
        x->id.proto = proto;
        x->id.spi = sa->sadb_sa_spi;
-       x->props.replay_window = sa->sadb_sa_replay;
+       x->props.replay_window = min_t(unsigned int, sa->sadb_sa_replay,
+                                       (sizeof(x->replay.bitmap) * 8));
        if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)
                x->props.flags |= XFRM_STATE_NOECN;
        if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP)
index feae495a0a30accd9eb5a88baf1c23095bb38d66..b076e8309bc2722885c27108316634f064cb44ff 100644 (file)
@@ -115,6 +115,11 @@ struct l2tp_net {
 static void l2tp_session_set_header_len(struct l2tp_session *session, int version);
 static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
 
+static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
+{
+       return sk->sk_user_data;
+}
+
 static inline struct l2tp_net *l2tp_pernet(struct net *net)
 {
        BUG_ON(!net);
@@ -504,7 +509,7 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
                return 0;
 
 #if IS_ENABLED(CONFIG_IPV6)
-       if (sk->sk_family == PF_INET6) {
+       if (sk->sk_family == PF_INET6 && !l2tp_tunnel(sk)->v4mapped) {
                if (!uh->check) {
                        LIMIT_NETDEBUG(KERN_INFO "L2TP: IPv6: checksum is 0\n");
                        return 1;
@@ -1128,7 +1133,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
        /* Queue the packet to IP for output */
        skb->local_df = 1;
 #if IS_ENABLED(CONFIG_IPV6)
-       if (skb->sk->sk_family == PF_INET6)
+       if (skb->sk->sk_family == PF_INET6 && !tunnel->v4mapped)
                error = inet6_csk_xmit(skb, NULL);
        else
 #endif
@@ -1255,7 +1260,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
 
                /* Calculate UDP checksum if configured to do so */
 #if IS_ENABLED(CONFIG_IPV6)
-               if (sk->sk_family == PF_INET6)
+               if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
                        l2tp_xmit_ipv6_csum(sk, skb, udp_len);
                else
 #endif
@@ -1304,10 +1309,9 @@ EXPORT_SYMBOL_GPL(l2tp_xmit_skb);
  */
 static void l2tp_tunnel_destruct(struct sock *sk)
 {
-       struct l2tp_tunnel *tunnel;
+       struct l2tp_tunnel *tunnel = l2tp_tunnel(sk);
        struct l2tp_net *pn;
 
-       tunnel = sk->sk_user_data;
        if (tunnel == NULL)
                goto end;
 
@@ -1675,7 +1679,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
        }
 
        /* Check if this socket has already been prepped */
-       tunnel = (struct l2tp_tunnel *)sk->sk_user_data;
+       tunnel = l2tp_tunnel(sk);
        if (tunnel != NULL) {
                /* This socket has already been prepped */
                err = -EBUSY;
@@ -1704,6 +1708,24 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
        if (cfg != NULL)
                tunnel->debug = cfg->debug;
 
+#if IS_ENABLED(CONFIG_IPV6)
+       if (sk->sk_family == PF_INET6) {
+               struct ipv6_pinfo *np = inet6_sk(sk);
+
+               if (ipv6_addr_v4mapped(&np->saddr) &&
+                   ipv6_addr_v4mapped(&np->daddr)) {
+                       struct inet_sock *inet = inet_sk(sk);
+
+                       tunnel->v4mapped = true;
+                       inet->inet_saddr = np->saddr.s6_addr32[3];
+                       inet->inet_rcv_saddr = np->rcv_saddr.s6_addr32[3];
+                       inet->inet_daddr = np->daddr.s6_addr32[3];
+               } else {
+                       tunnel->v4mapped = false;
+               }
+       }
+#endif
+
        /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
        tunnel->encap = encap;
        if (encap == L2TP_ENCAPTYPE_UDP) {
@@ -1712,7 +1734,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
                udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;
                udp_sk(sk)->encap_destroy = l2tp_udp_encap_destroy;
 #if IS_ENABLED(CONFIG_IPV6)
-               if (sk->sk_family == PF_INET6)
+               if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
                        udpv6_encap_enable();
                else
 #endif
index 66a559b104b6a7af0bc5c76770c32ad0f816670c..6f251cbc2ed787409794f3f5063312ef7b9e9c72 100644 (file)
@@ -194,6 +194,9 @@ struct l2tp_tunnel {
        struct sock             *sock;          /* Parent socket */
        int                     fd;             /* Parent fd, if tunnel socket
                                                 * was created by userspace */
+#if IS_ENABLED(CONFIG_IPV6)
+       bool                    v4mapped;
+#endif
 
        struct work_struct      del_work;
 
index 5ebee2ded9e9a8f40d2a282600b47f6cb088a910..8c46b271064a43607190198e76c1e1ff4c540396 100644 (file)
@@ -353,7 +353,9 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
                goto error_put_sess_tun;
        }
 
+       local_bh_disable();
        l2tp_xmit_skb(session, skb, session->hdr_len);
+       local_bh_enable();
 
        sock_put(ps->tunnel_sock);
        sock_put(sk);
@@ -422,7 +424,9 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        skb->data[0] = ppph[0];
        skb->data[1] = ppph[1];
 
+       local_bh_disable();
        l2tp_xmit_skb(session, skb, session->hdr_len);
+       local_bh_enable();
 
        sock_put(sk_tun);
        sock_put(sk);
index 2e7855a1b10d17198adcfec98d550838daef80bd..629dee7ec9bfbae6dc0f00cdaf91b58968980c99 100644 (file)
@@ -3518,7 +3518,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
                return -EINVAL;
        }
        band = chanctx_conf->def.chan->band;
-       sta = sta_info_get(sdata, peer);
+       sta = sta_info_get_bss(sdata, peer);
        if (sta) {
                qos = test_sta_flag(sta, WLAN_STA_WME);
        } else {
index b6186517ec567e85eb986d77dd6ff72c36a4379d..611abfcfb5ebba3c135edbcf8b2efb4a47f38374 100644 (file)
@@ -893,6 +893,8 @@ struct tpt_led_trigger {
  *     that the scan completed.
  * @SCAN_ABORTED: Set for our scan work function when the driver reported
  *     a scan complete for an aborted scan.
+ * @SCAN_HW_CANCELLED: Set for our scan work function when the scan is being
+ *     cancelled.
  */
 enum {
        SCAN_SW_SCANNING,
@@ -900,6 +902,7 @@ enum {
        SCAN_ONCHANNEL_SCANNING,
        SCAN_COMPLETED,
        SCAN_ABORTED,
+       SCAN_HW_CANCELLED,
 };
 
 /**
index acd1f71adc0386ab588f0939309e2367330f2b29..0c2a29484c07cdaaaf716b1f5ba0184d1047ee68 100644 (file)
@@ -394,6 +394,8 @@ void ieee80211_sw_roc_work(struct work_struct *work)
 
                if (started)
                        ieee80211_start_next_roc(local);
+               else if (list_empty(&local->roc_list))
+                       ieee80211_run_deferred_scan(local);
        }
 
  out_unlock:
index 54395d7583ba70e31111b08092c88d2901be7815..674eac1f996c9ddd664f2f70022514efc228b109 100644 (file)
@@ -3056,6 +3056,9 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
        case NL80211_IFTYPE_ADHOC:
                if (!bssid)
                        return 0;
+               if (ether_addr_equal(sdata->vif.addr, hdr->addr2) ||
+                   ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2))
+                       return 0;
                if (ieee80211_is_beacon(hdr->frame_control)) {
                        return 1;
                } else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) {
index 08afe74b98f4b6cbdda21ed3d8ff68b9a95fa64b..d2d17a4492245953413c5742d22d2af9e15fd74d 100644 (file)
@@ -238,6 +238,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
        enum ieee80211_band band;
        int i, ielen, n_chans;
 
+       if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
+               return false;
+
        do {
                if (local->hw_scan_band == IEEE80211_NUM_BANDS)
                        return false;
@@ -940,7 +943,23 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
        if (!local->scan_req)
                goto out;
 
+       /*
+        * We have a scan running and the driver already reported completion,
+        * but the worker hasn't run yet or is stuck on the mutex - mark it as
+        * cancelled.
+        */
+       if (test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+           test_bit(SCAN_COMPLETED, &local->scanning)) {
+               set_bit(SCAN_HW_CANCELLED, &local->scanning);
+               goto out;
+       }
+
        if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
+               /*
+                * Make sure that __ieee80211_scan_completed doesn't trigger a
+                * scan on another band.
+                */
+               set_bit(SCAN_HW_CANCELLED, &local->scanning);
                if (local->ops->cancel_hw_scan)
                        drv_cancel_hw_scan(local,
                                rcu_dereference_protected(local->scan_sdata,
index 368837fe3b800e87f408039ef4d36e84bfaa7069..78dc2e99027e06b8a9fc37f5bdf7f06483476f2b 100644 (file)
@@ -180,6 +180,9 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 
+       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               sta->last_rx = jiffies;
+
        if (ieee80211_is_data_qos(mgmt->frame_control)) {
                struct ieee80211_hdr *hdr = (void *) skb->data;
                u8 *qc = ieee80211_get_qos_ctl(hdr);
index 3456c0486b482bfb44a6f797ebaff5d70bde4940..70b5a05c0a4e12b8cb36252465171ffbc7ef1686 100644 (file)
@@ -1120,7 +1120,8 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
                tx->sta = rcu_dereference(sdata->u.vlan.sta);
                if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
                        return TX_DROP;
-       } else if (info->flags & IEEE80211_TX_CTL_INJECTED ||
+       } else if (info->flags & (IEEE80211_TX_CTL_INJECTED |
+                                 IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||
                   tx->sdata->control_port_protocol == tx->skb->protocol) {
                tx->sta = sta_info_get_bss(sdata, hdr->addr1);
        }
index e1b34a18b24344cb95a642c436fc5322054207ce..69e4ef5348a0e916b76fa87de9d83402ec99a5e4 100644 (file)
@@ -2103,7 +2103,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
-       int rate, skip, shift;
+       int rate, shift;
        u8 i, exrates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
        u32 rate_flags;
@@ -2131,14 +2131,11 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
                pos = skb_put(skb, exrates + 2);
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
                *pos++ = exrates;
-               skip = 0;
                for (i = 8; i < sband->n_bitrates; i++) {
                        u8 basic = 0;
                        if ((rate_flags & sband->bitrates[i].flags)
                            != rate_flags)
                                continue;
-                       if (skip++ < 8)
-                               continue;
                        if (need_basic && basic_rates & BIT(i))
                                basic = 0x80;
                        rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
@@ -2241,6 +2238,10 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
        }
 
        rate = cfg80211_calculate_bitrate(&ri);
+       if (WARN_ONCE(!rate,
+                     "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n",
+                     status->flag, status->rate_idx, status->vht_nss))
+               return 0;
 
        /* rewind from end of MPDU */
        if (status->flag & RX_FLAG_MACTIME_END)
index bdebd03bc8cd448a75ca94db1c88ea461f72093a..70866d192efc9351f2fcafe3ef23ce0c131fc0ba 100644 (file)
@@ -778,8 +778,8 @@ static int callforward_do_filter(const union nf_inet_addr *src,
                                   flowi6_to_flowi(&fl1), false)) {
                        if (!afinfo->route(&init_net, (struct dst_entry **)&rt2,
                                           flowi6_to_flowi(&fl2), false)) {
-                               if (!memcmp(&rt1->rt6i_gateway, &rt2->rt6i_gateway,
-                                           sizeof(rt1->rt6i_gateway)) &&
+                               if (ipv6_addr_equal(rt6_nexthop(rt1),
+                                                   rt6_nexthop(rt2)) &&
                                    rt1->dst.dev == rt2->dst.dev)
                                        ret = 1;
                                dst_release(&rt2->dst);
index 8b03028cca69b616df298e0ffbfdeb247441a341..227aa11e8409bb18be93c0c432a4d71cfb6e8f38 100644 (file)
@@ -845,8 +845,13 @@ xt_replace_table(struct xt_table *table,
                return NULL;
        }
 
-       table->private = newinfo;
        newinfo->initial_entries = private->initial_entries;
+       /*
+        * Ensure contents of newinfo are visible before assigning to
+        * private.
+        */
+       smp_wmb();
+       table->private = newinfo;
 
        /*
         * Even though table entries have now been swapped, other CPU's
index 1e2fae32f81b9118f50a7c0d403cee2a324af252..ed00fef58996a8d702b6020720daf679caa1af15 100644 (file)
@@ -147,6 +147,7 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_NFQ_info_v3 *info = par->targinfo;
        u32 queue = info->queuenum;
+       int ret;
 
        if (info->queues_total > 1) {
                if (info->flags & NFQ_FLAG_CPU_FANOUT) {
@@ -157,7 +158,11 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
                        queue = nfqueue_hash(skb, par);
        }
 
-       return NF_QUEUE_NR(queue);
+       ret = NF_QUEUE_NR(queue);
+       if (info->flags & NFQ_FLAG_BYPASS)
+               ret |= NF_VERDICT_FLAG_QUEUE_BYPASS;
+
+       return ret;
 }
 
 static struct xt_target nfqueue_tg_reg[] __read_mostly = {
index c3235675f35997169ad4961722344537005a8f6e..5c2dab27610962caed49d6017a6cda5b5ac855fc 100644 (file)
@@ -65,8 +65,7 @@ void ovs_dp_notify_wq(struct work_struct *work)
                                        continue;
 
                                netdev_vport = netdev_vport_priv(vport);
-                               if (netdev_vport->dev->reg_state == NETREG_UNREGISTERED ||
-                                   netdev_vport->dev->reg_state == NETREG_UNREGISTERING)
+                               if (!(netdev_vport->dev->priv_flags & IFF_OVS_DATAPATH))
                                        dp_detach_port_notify(vport);
                        }
                }
@@ -88,6 +87,10 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
                return NOTIFY_DONE;
 
        if (event == NETDEV_UNREGISTER) {
+               /* upper_dev_unlink and decrement promisc immediately */
+               ovs_netdev_detach_dev(vport);
+
+               /* schedule vport destroy, dev_put and genl notification */
                ovs_net = net_generic(dev_net(dev), ovs_net_id);
                queue_work(system_wq, &ovs_net->dp_notify_work);
        }
index 09d93c13cfd69a295d533cad35ab857c9eeee2f0..d21f77d875ba7b690a0c46231e0b539172488562 100644 (file)
@@ -150,15 +150,25 @@ static void free_port_rcu(struct rcu_head *rcu)
        ovs_vport_free(vport_from_priv(netdev_vport));
 }
 
-static void netdev_destroy(struct vport *vport)
+void ovs_netdev_detach_dev(struct vport *vport)
 {
        struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
 
-       rtnl_lock();
+       ASSERT_RTNL();
        netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
        netdev_rx_handler_unregister(netdev_vport->dev);
-       netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp));
+       netdev_upper_dev_unlink(netdev_vport->dev,
+                               netdev_master_upper_dev_get(netdev_vport->dev));
        dev_set_promiscuity(netdev_vport->dev, -1);
+}
+
+static void netdev_destroy(struct vport *vport)
+{
+       struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
+
+       rtnl_lock();
+       if (netdev_vport->dev->priv_flags & IFF_OVS_DATAPATH)
+               ovs_netdev_detach_dev(vport);
        rtnl_unlock();
 
        call_rcu(&netdev_vport->rcu, free_port_rcu);
index dd298b5c5cdb25e1a721e5b845353ce055c02e60..8df01c1127e546d06ba2f8c3fde44b51c58ad0de 100644 (file)
@@ -39,5 +39,6 @@ netdev_vport_priv(const struct vport *vport)
 }
 
 const char *ovs_netdev_get_name(const struct vport *);
+void ovs_netdev_detach_dev(struct vport *);
 
 #endif /* vport_netdev.h */
index a2fef8b10b960c15d29556b7dc832737c848b3b6..fdc041c5785360731154521fb3492dba825776ff 100644 (file)
@@ -255,6 +255,7 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
                                     f->socket_hash != sk->sk_hash)) {
                                f->credit = q->initial_quantum;
                                f->socket_hash = sk->sk_hash;
+                               f->time_next_packet = 0ULL;
                        }
                        return f;
                }
@@ -472,20 +473,16 @@ begin:
        if (f->credit > 0 || !q->rate_enable)
                goto out;
 
-       if (skb->sk && skb->sk->sk_state != TCP_TIME_WAIT) {
-               rate = skb->sk->sk_pacing_rate ?: q->flow_default_rate;
+       rate = q->flow_max_rate;
+       if (skb->sk && skb->sk->sk_state != TCP_TIME_WAIT)
+               rate = min(skb->sk->sk_pacing_rate, rate);
 
-               rate = min(rate, q->flow_max_rate);
-       } else {
-               rate = q->flow_max_rate;
-               if (rate == ~0U)
-                       goto out;
-       }
-       if (rate) {
+       if (rate != ~0U) {
                u32 plen = max(qdisc_pkt_len(skb), q->quantum);
                u64 len = (u64)plen * NSEC_PER_SEC;
 
-               do_div(len, rate);
+               if (likely(rate))
+                       do_div(len, rate);
                /* Since socket rate can change later,
                 * clamp the delay to 125 ms.
                 * TODO: maybe segment the too big skb, as in commit
@@ -656,7 +653,7 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt)
                q->quantum = nla_get_u32(tb[TCA_FQ_QUANTUM]);
 
        if (tb[TCA_FQ_INITIAL_QUANTUM])
-               q->quantum = nla_get_u32(tb[TCA_FQ_INITIAL_QUANTUM]);
+               q->initial_quantum = nla_get_u32(tb[TCA_FQ_INITIAL_QUANTUM]);
 
        if (tb[TCA_FQ_FLOW_DEFAULT_RATE])
                q->flow_default_rate = nla_get_u32(tb[TCA_FQ_FLOW_DEFAULT_RATE]);
@@ -735,12 +732,14 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
        if (opts == NULL)
                goto nla_put_failure;
 
+       /* TCA_FQ_FLOW_DEFAULT_RATE is not used anymore,
+        * do not bother giving its value
+        */
        if (nla_put_u32(skb, TCA_FQ_PLIMIT, sch->limit) ||
            nla_put_u32(skb, TCA_FQ_FLOW_PLIMIT, q->flow_plimit) ||
            nla_put_u32(skb, TCA_FQ_QUANTUM, q->quantum) ||
            nla_put_u32(skb, TCA_FQ_INITIAL_QUANTUM, q->initial_quantum) ||
            nla_put_u32(skb, TCA_FQ_RATE_ENABLE, q->rate_enable) ||
-           nla_put_u32(skb, TCA_FQ_FLOW_DEFAULT_RATE, q->flow_default_rate) ||
            nla_put_u32(skb, TCA_FQ_FLOW_MAX_RATE, q->flow_max_rate) ||
            nla_put_u32(skb, TCA_FQ_BUCKETS_LOG, q->fq_trees_log))
                goto nla_put_failure;
index a6d788d45216a6f286e5aaea0cdb0587cd0f7848..b87e83d0747821bc2251c3b099f91a3358c211d9 100644 (file)
@@ -358,6 +358,21 @@ static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sche
        return PSCHED_NS2TICKS(ticks);
 }
 
+static void tfifo_reset(struct Qdisc *sch)
+{
+       struct netem_sched_data *q = qdisc_priv(sch);
+       struct rb_node *p;
+
+       while ((p = rb_first(&q->t_root))) {
+               struct sk_buff *skb = netem_rb_to_skb(p);
+
+               rb_erase(p, &q->t_root);
+               skb->next = NULL;
+               skb->prev = NULL;
+               kfree_skb(skb);
+       }
+}
+
 static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
@@ -520,6 +535,7 @@ static unsigned int netem_drop(struct Qdisc *sch)
                        skb->next = NULL;
                        skb->prev = NULL;
                        len = qdisc_pkt_len(skb);
+                       sch->qstats.backlog -= len;
                        kfree_skb(skb);
                }
        }
@@ -609,6 +625,7 @@ static void netem_reset(struct Qdisc *sch)
        struct netem_sched_data *q = qdisc_priv(sch);
 
        qdisc_reset_queue(sch);
+       tfifo_reset(sch);
        if (q->qdisc)
                qdisc_reset(q->qdisc);
        qdisc_watchdog_cancel(&q->watchdog);
index e7b2d4fe2b6a120c66b3e869d796eb6dba69c2d2..96a55910262c4e958bef93d203ce802dede835df 100644 (file)
@@ -279,7 +279,9 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
                sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port));
                rcu_read_lock();
                list_for_each_entry_rcu(laddr, &bp->address_list, list) {
-                       if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
+                       if (!laddr->valid || laddr->state == SCTP_ADDR_DEL ||
+                           (laddr->state != SCTP_ADDR_SRC &&
+                            !asoc->src_out_of_asoc_ok))
                                continue;
 
                        /* Do not compare against v4 addrs */
index 0ac3a65daccb71cd78ae5e2c52696c33df7e16e4..319137340d158df36094c45b8eae1fc13df50825 100644 (file)
@@ -536,7 +536,8 @@ int sctp_packet_transmit(struct sctp_packet *packet)
         * by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
         */
        if (!sctp_checksum_disable) {
-               if (!(dst->dev->features & NETIF_F_SCTP_CSUM)) {
+               if (!(dst->dev->features & NETIF_F_SCTP_CSUM) ||
+                   (dst_xfrm(dst) != NULL) || packet->ipfragok) {
                        __u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len);
 
                        /* 3) Put the resultant value into the checksum field in the
index 666c668427996903b9baf185a831c1c10c75d8cb..1a6eef39ab2fcd169dde99a6494758793f5c88fc 100644 (file)
@@ -860,7 +860,6 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
            (!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK))
                return;
 
-       BUG_ON(asoc->peer.primary_path == NULL);
        sctp_unhash_established(asoc);
        sctp_association_free(asoc);
 }
index ebed4b68f768a1afccea45ea7dc22ef555f3f679..c226aceee65b8b8c59d93d6a133a04c86177ceef 100644 (file)
@@ -1964,6 +1964,16 @@ struct used_address {
        unsigned int name_len;
 };
 
+static int copy_msghdr_from_user(struct msghdr *kmsg,
+                                struct msghdr __user *umsg)
+{
+       if (copy_from_user(kmsg, umsg, sizeof(struct msghdr)))
+               return -EFAULT;
+       if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+               return -EINVAL;
+       return 0;
+}
+
 static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
                         struct msghdr *msg_sys, unsigned int flags,
                         struct used_address *used_address)
@@ -1982,8 +1992,11 @@ static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
        if (MSG_CMSG_COMPAT & flags) {
                if (get_compat_msghdr(msg_sys, msg_compat))
                        return -EFAULT;
-       } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr)))
-               return -EFAULT;
+       } else {
+               err = copy_msghdr_from_user(msg_sys, msg);
+               if (err)
+                       return err;
+       }
 
        if (msg_sys->msg_iovlen > UIO_FASTIOV) {
                err = -EMSGSIZE;
@@ -2191,8 +2204,11 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
        if (MSG_CMSG_COMPAT & flags) {
                if (get_compat_msghdr(msg_sys, msg_compat))
                        return -EFAULT;
-       } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr)))
-               return -EFAULT;
+       } else {
+               err = copy_msghdr_from_user(msg_sys, msg);
+               if (err)
+                       return err;
+       }
 
        if (msg_sys->msg_iovlen > UIO_FASTIOV) {
                err = -EMSGSIZE;
index 86de99ad297605d04356f9efd7be174d2f7c7993..c1f403bed683ee8653356870f35457fe6e7f18eb 100644 (file)
@@ -1246,6 +1246,15 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)
        return 0;
 }
 
+static void unix_sock_inherit_flags(const struct socket *old,
+                                   struct socket *new)
+{
+       if (test_bit(SOCK_PASSCRED, &old->flags))
+               set_bit(SOCK_PASSCRED, &new->flags);
+       if (test_bit(SOCK_PASSSEC, &old->flags))
+               set_bit(SOCK_PASSSEC, &new->flags);
+}
+
 static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
 {
        struct sock *sk = sock->sk;
@@ -1280,6 +1289,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
        /* attach accepted sock to socket */
        unix_state_lock(tsk);
        newsock->state = SS_CONNECTED;
+       unix_sock_inherit_flags(sock, newsock);
        sock_graft(tsk, newsock);
        unix_state_unlock(tsk);
        return 0;
index d591091603bfe4da1d6a87810287bdd5aaecc6da..86fa0f3b2cafa46d47db9cd03e46dfe89c22d238 100644 (file)
@@ -124,6 +124,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r
        rep->udiag_family = AF_UNIX;
        rep->udiag_type = sk->sk_type;
        rep->udiag_state = sk->sk_state;
+       rep->pad = 0;
        rep->udiag_ino = sk_ino;
        sock_diag_save_cookie(sk, rep->udiag_cookie);
 
index 67153964aad2059652ffd34d79c956585cc9c1e3..aff959e5a1b360e7cb467cade7f7d617544b3909 100644 (file)
@@ -566,18 +566,13 @@ int wiphy_register(struct wiphy *wiphy)
        /* check and set up bitrates */
        ieee80211_set_bitrate_flags(wiphy);
 
-
+       rtnl_lock();
        res = device_add(&rdev->wiphy.dev);
-       if (res)
-               return res;
-
-       res = rfkill_register(rdev->rfkill);
        if (res) {
-               device_del(&rdev->wiphy.dev);
+               rtnl_unlock();
                return res;
        }
 
-       rtnl_lock();
        /* set up regulatory info */
        wiphy_regulatory_register(wiphy);
 
@@ -606,6 +601,15 @@ int wiphy_register(struct wiphy *wiphy)
 
        rdev->wiphy.registered = true;
        rtnl_unlock();
+
+       res = rfkill_register(rdev->rfkill);
+       if (res) {
+               rfkill_destroy(rdev->rfkill);
+               rdev->rfkill = NULL;
+               wiphy_unregister(&rdev->wiphy);
+               return res;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(wiphy_register);
@@ -640,7 +644,8 @@ void wiphy_unregister(struct wiphy *wiphy)
                rtnl_unlock();
                __count == 0; }));
 
-       rfkill_unregister(rdev->rfkill);
+       if (rdev->rfkill)
+               rfkill_unregister(rdev->rfkill);
 
        rtnl_lock();
        rdev->wiphy.registered = false;
@@ -953,8 +958,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
        case NETDEV_PRE_UP:
                if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
                        return notifier_from_errno(-EOPNOTSUPP);
-               if (rfkill_blocked(rdev->rfkill))
-                       return notifier_from_errno(-ERFKILL);
                ret = cfg80211_can_add_interface(rdev, wdev->iftype);
                if (ret)
                        return notifier_from_errno(ret);
index 9ad43c619c54830f915193daa60eadef92b5bda5..3159e9c284c5b10c3b7cd0cf4d384d278b0a6245 100644 (file)
@@ -411,6 +411,9 @@ static inline int
 cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
                           enum nl80211_iftype iftype)
 {
+       if (rfkill_blocked(rdev->rfkill))
+               return -ERFKILL;
+
        return cfg80211_can_change_interface(rdev, NULL, iftype);
 }
 
index 39bff7d367687fbdd4d220d76e34d66e4e586a9c..403fe29c024db86f732b0c0d74b8d97d4cfc84bf 100644 (file)
@@ -263,6 +263,8 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
                                if (chan->flags & IEEE80211_CHAN_DISABLED)
                                        continue;
                                wdev->wext.ibss.chandef.chan = chan;
+                               wdev->wext.ibss.chandef.center_freq1 =
+                                       chan->center_freq;
                                break;
                        }
 
@@ -347,6 +349,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
        if (chan) {
                wdev->wext.ibss.chandef.chan = chan;
                wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+               wdev->wext.ibss.chandef.center_freq1 = freq;
                wdev->wext.ibss.channel_fixed = true;
        } else {
                /* cfg80211_ibss_wext_join will pick one if needed */
index af8d84a4a5b2a05fab2de732722e6535c8e699d0..626dc3b5fd8d205305b8ddfecaf106c33adcb9d6 100644 (file)
@@ -2421,7 +2421,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                change = true;
        }
 
-       if (flags && (*flags & NL80211_MNTR_FLAG_ACTIVE) &&
+       if (flags && (*flags & MONITOR_FLAG_ACTIVE) &&
            !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
                return -EOPNOTSUPP;
 
@@ -2483,7 +2483,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
 
-       if (!err && (flags & NL80211_MNTR_FLAG_ACTIVE) &&
+       if (!err && (flags & MONITOR_FLAG_ACTIVE) &&
            !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
                return -EOPNOTSUPP;
 
index 7d604c06c3dc38d1155366a52f184971be1197e3..a271c27fac774ce987c0db6f1330ffbfca6dc7f7 100644 (file)
@@ -97,6 +97,10 @@ int ieee80211_radiotap_iterator_init(
        struct ieee80211_radiotap_header *radiotap_header,
        int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
 {
+       /* check the radiotap header can actually be present */
+       if (max_length < sizeof(struct ieee80211_radiotap_header))
+               return -EINVAL;
+
        /* Linux only supports version 0 radiotap format */
        if (radiotap_header->it_version)
                return -EINVAL;
@@ -131,7 +135,8 @@ int ieee80211_radiotap_iterator_init(
                         */
 
                        if ((unsigned long)iterator->_arg -
-                           (unsigned long)iterator->_rtheader >
+                           (unsigned long)iterator->_rtheader +
+                           sizeof(uint32_t) >
                            (unsigned long)iterator->_max_length)
                                return -EINVAL;
                }
index c959312c45e3e050307ac5126a59f8478babc7ae..e2fa133f9fba225656fa62f4e9fb61e745a4d46a 100644 (file)
@@ -16,8 +16,8 @@ config X25
          if you want that) and the lower level data link layer protocol LAPB
          (say Y to "LAPB Data Link Driver" below if you want that).
 
-         You can read more about X.25 at <http://www.sangoma.com/x25.htm> and
-         <http://www.cisco.com/univercd/cc/td/doc/product/software/ios11/cbook/cx25.htm>.
+         You can read more about X.25 at <http://www.sangoma.com/tutorials/x25/> and
+         <http://docwiki.cisco.com/wiki/X.25>.
          Information about X.25 for Linux is contained in the files
          <file:Documentation/networking/x25.txt> and
          <file:Documentation/networking/x25-iface.txt>.
index 2906d520eea7c2b7636fc94f3a60f5131701f57d..3be02b680268d1ccb7e3672a92777b12963b8562 100644 (file)
@@ -141,14 +141,14 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
        const int plen = skb->len;
        int dlen = IPCOMP_SCRATCH_SIZE;
        u8 *start = skb->data;
-       const int cpu = get_cpu();
-       u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
-       struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
+       struct crypto_comp *tfm;
+       u8 *scratch;
        int err;
 
        local_bh_disable();
+       scratch = *this_cpu_ptr(ipcomp_scratches);
+       tfm = *this_cpu_ptr(ipcd->tfms);
        err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
-       local_bh_enable();
        if (err)
                goto out;
 
@@ -158,13 +158,13 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
        }
 
        memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
-       put_cpu();
+       local_bh_enable();
 
        pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr));
        return 0;
 
 out:
-       put_cpu();
+       local_bh_enable();
        return err;
 }
 
index ed38d5d81f9e1f4890cf36447713badd48691489..76e1873811d4cac098cd4d563e865e847997a4a4 100644 (file)
@@ -334,7 +334,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
 
        atomic_inc(&policy->genid);
 
-       del_timer(&policy->polq.hold_timer);
+       if (del_timer(&policy->polq.hold_timer))
+               xfrm_pol_put(policy);
        xfrm_queue_purge(&policy->polq.hold_queue);
 
        if (del_timer(&policy->timer))
@@ -589,7 +590,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,
 
        spin_lock_bh(&pq->hold_queue.lock);
        skb_queue_splice_init(&pq->hold_queue, &list);
-       del_timer(&pq->hold_timer);
+       if (del_timer(&pq->hold_timer))
+               xfrm_pol_put(old);
        spin_unlock_bh(&pq->hold_queue.lock);
 
        if (skb_queue_empty(&list))
@@ -600,7 +602,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,
        spin_lock_bh(&pq->hold_queue.lock);
        skb_queue_splice(&list, &pq->hold_queue);
        pq->timeout = XFRM_QUEUE_TMO_MIN;
-       mod_timer(&pq->hold_timer, jiffies);
+       if (!mod_timer(&pq->hold_timer, jiffies))
+               xfrm_pol_hold(new);
        spin_unlock_bh(&pq->hold_queue.lock);
 }
 
@@ -1769,6 +1772,10 @@ static void xfrm_policy_queue_process(unsigned long arg)
 
        spin_lock(&pq->hold_queue.lock);
        skb = skb_peek(&pq->hold_queue);
+       if (!skb) {
+               spin_unlock(&pq->hold_queue.lock);
+               goto out;
+       }
        dst = skb_dst(skb);
        sk = skb->sk;
        xfrm_decode_session(skb, &fl, dst->ops->family);
@@ -1787,8 +1794,9 @@ static void xfrm_policy_queue_process(unsigned long arg)
                        goto purge_queue;
 
                pq->timeout = pq->timeout << 1;
-               mod_timer(&pq->hold_timer, jiffies + pq->timeout);
-               return;
+               if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout))
+                       xfrm_pol_hold(pol);
+       goto out;
        }
 
        dst_release(dst);
@@ -1819,11 +1827,14 @@ static void xfrm_policy_queue_process(unsigned long arg)
                err = dst_output(skb);
        }
 
+out:
+       xfrm_pol_put(pol);
        return;
 
 purge_queue:
        pq->timeout = 0;
        xfrm_queue_purge(&pq->hold_queue);
+       xfrm_pol_put(pol);
 }
 
 static int xdst_queue_output(struct sk_buff *skb)
@@ -1831,7 +1842,8 @@ static int xdst_queue_output(struct sk_buff *skb)
        unsigned long sched_next;
        struct dst_entry *dst = skb_dst(skb);
        struct xfrm_dst *xdst = (struct xfrm_dst *) dst;
-       struct xfrm_policy_queue *pq = &xdst->pols[0]->polq;
+       struct xfrm_policy *pol = xdst->pols[0];
+       struct xfrm_policy_queue *pq = &pol->polq;
 
        if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) {
                kfree_skb(skb);
@@ -1850,10 +1862,12 @@ static int xdst_queue_output(struct sk_buff *skb)
        if (del_timer(&pq->hold_timer)) {
                if (time_before(pq->hold_timer.expires, sched_next))
                        sched_next = pq->hold_timer.expires;
+               xfrm_pol_put(pol);
        }
 
        __skb_queue_tail(&pq->hold_queue, skb);
-       mod_timer(&pq->hold_timer, sched_next);
+       if (!mod_timer(&pq->hold_timer, sched_next))
+               xfrm_pol_hold(pol);
 
        spin_unlock_bh(&pq->hold_queue.lock);
 
index 8dafe6d3c6e41ebd20e5d0347d4ab9b0e6326a34..dab57daae40856030790fa8070068a59d82220af 100644 (file)
@@ -61,9 +61,9 @@ static void xfrm_replay_notify(struct xfrm_state *x, int event)
 
        switch (event) {
        case XFRM_REPLAY_UPDATE:
-               if (x->replay_maxdiff &&
-                   (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
-                   (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
+               if (!x->replay_maxdiff ||
+                   ((x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
+                   (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) {
                        if (x->xflags & XFRM_TIME_DEFER)
                                event = XFRM_REPLAY_TIMEOUT;
                        else
@@ -129,8 +129,7 @@ static int xfrm_replay_check(struct xfrm_state *x,
                return 0;
 
        diff = x->replay.seq - seq;
-       if (diff >= min_t(unsigned int, x->props.replay_window,
-                         sizeof(x->replay.bitmap) * 8)) {
+       if (diff >= x->props.replay_window) {
                x->stats.replay_window++;
                goto err;
        }
@@ -302,9 +301,10 @@ static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)
 
        switch (event) {
        case XFRM_REPLAY_UPDATE:
-               if (x->replay_maxdiff &&
-                   (replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
-                   (replay_esn->oseq - preplay_esn->oseq < x->replay_maxdiff)) {
+               if (!x->replay_maxdiff ||
+                   ((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
+                   (replay_esn->oseq - preplay_esn->oseq
+                    < x->replay_maxdiff))) {
                        if (x->xflags & XFRM_TIME_DEFER)
                                event = XFRM_REPLAY_TIMEOUT;
                        else
@@ -353,28 +353,30 @@ static void xfrm_replay_notify_esn(struct xfrm_state *x, int event)
 
        switch (event) {
        case XFRM_REPLAY_UPDATE:
-               if (!x->replay_maxdiff)
-                       break;
-
-               if (replay_esn->seq_hi == preplay_esn->seq_hi)
-                       seq_diff = replay_esn->seq - preplay_esn->seq;
-               else
-                       seq_diff = ~preplay_esn->seq + replay_esn->seq + 1;
-
-               if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
-                       oseq_diff = replay_esn->oseq - preplay_esn->oseq;
-               else
-                       oseq_diff = ~preplay_esn->oseq + replay_esn->oseq + 1;
-
-               if (seq_diff < x->replay_maxdiff &&
-                   oseq_diff < x->replay_maxdiff) {
+               if (x->replay_maxdiff) {
+                       if (replay_esn->seq_hi == preplay_esn->seq_hi)
+                               seq_diff = replay_esn->seq - preplay_esn->seq;
+                       else
+                               seq_diff = ~preplay_esn->seq + replay_esn->seq
+                                          + 1;
 
-                       if (x->xflags & XFRM_TIME_DEFER)
-                               event = XFRM_REPLAY_TIMEOUT;
+                       if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
+                               oseq_diff = replay_esn->oseq
+                                           - preplay_esn->oseq;
                        else
-                               return;
+                               oseq_diff = ~preplay_esn->oseq
+                                           + replay_esn->oseq + 1;
+
+                       if (seq_diff >= x->replay_maxdiff ||
+                           oseq_diff >= x->replay_maxdiff)
+                               break;
                }
 
+               if (x->xflags & XFRM_TIME_DEFER)
+                       event = XFRM_REPLAY_TIMEOUT;
+               else
+                       return;
+
                break;
 
        case XFRM_REPLAY_TIMEOUT:
index 3f565e495ac68cea83e1d52cf7db7e820fe777ad..f964d4c00ffb53457aa46b24f0225249c1d46b7c 100644 (file)
@@ -446,7 +446,8 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
        memcpy(&x->sel, &p->sel, sizeof(x->sel));
        memcpy(&x->lft, &p->lft, sizeof(x->lft));
        x->props.mode = p->mode;
-       x->props.replay_window = p->replay_window;
+       x->props.replay_window = min_t(unsigned int, p->replay_window,
+                                       sizeof(x->replay.bitmap) * 8);
        x->props.reqid = p->reqid;
        x->props.family = p->family;
        memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
@@ -1856,7 +1857,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (x->km.state != XFRM_STATE_VALID)
                goto out;
 
-       err = xfrm_replay_verify_len(x->replay_esn, rp);
+       err = xfrm_replay_verify_len(x->replay_esn, re);
        if (err)
                goto out;
 
index 487ac6f37ca23ce2d1e832fa177d74d4544386bf..9a11f9f799f499f2d34d1dea3dec48feb36db00f 100644 (file)
@@ -55,6 +55,7 @@ static struct sym_entry *table;
 static unsigned int table_size, table_cnt;
 static int all_symbols = 0;
 static char symbol_prefix_char = '\0';
+static unsigned long long kernel_start_addr = 0;
 
 int token_profit[0x10000];
 
@@ -65,7 +66,10 @@ unsigned char best_table_len[256];
 
 static void usage(void)
 {
-       fprintf(stderr, "Usage: kallsyms [--all-symbols] [--symbol-prefix=<prefix char>] < in.map > out.S\n");
+       fprintf(stderr, "Usage: kallsyms [--all-symbols] "
+                       "[--symbol-prefix=<prefix char>] "
+                       "[--page-offset=<CONFIG_PAGE_OFFSET>] "
+                       "< in.map > out.S\n");
        exit(1);
 }
 
@@ -194,6 +198,9 @@ static int symbol_valid(struct sym_entry *s)
        int i;
        int offset = 1;
 
+       if (s->addr < kernel_start_addr)
+               return 0;
+
        /* skip prefix char */
        if (symbol_prefix_char && *(s->sym + 1) == symbol_prefix_char)
                offset++;
@@ -646,6 +653,9 @@ int main(int argc, char **argv)
                                if ((*p == '"' && *(p+2) == '"') || (*p == '\'' && *(p+2) == '\''))
                                        p++;
                                symbol_prefix_char = *p;
+                       } else if (strncmp(argv[i], "--page-offset=", 14) == 0) {
+                               const char *p = &argv[i][14];
+                               kernel_start_addr = strtoull(p, NULL, 16);
                        } else
                                usage();
                }
index 014994936b1c2152c82f793ce646342159d78eb5..32b10f53d0b4cbad76b13ef86d547ad864ae19c2 100644 (file)
@@ -82,6 +82,8 @@ kallsyms()
                kallsymopt="${kallsymopt} --all-symbols"
        fi
 
+       kallsymopt="${kallsymopt} --page-offset=$CONFIG_PAGE_OFFSET"
+
        local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL}               \
                      ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
 
index 17f45e8aa89cd49561cce6c40cbc174f416d9c49..e1e9e0c999fefa854a507e623b821371ebf00b78 100644 (file)
@@ -49,6 +49,8 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
        struct snd_pcm *pcm;
 
        list_for_each_entry(pcm, &snd_pcm_devices, list) {
+               if (pcm->internal)
+                       continue;
                if (pcm->card == card && pcm->device == device)
                        return pcm;
        }
@@ -60,6 +62,8 @@ static int snd_pcm_next(struct snd_card *card, int device)
        struct snd_pcm *pcm;
 
        list_for_each_entry(pcm, &snd_pcm_devices, list) {
+               if (pcm->internal)
+                       continue;
                if (pcm->card == card && pcm->device > device)
                        return pcm->device;
                else if (pcm->card->number > card->number)
index 5b6c4e3c92ca40a7000909c1540d7d6787d36946..748c6a941963cbcb6108bc8aac90843cca7ee72f 100644 (file)
@@ -4864,8 +4864,8 @@ static void hda_power_work(struct work_struct *work)
        spin_unlock(&codec->power_lock);
 
        state = hda_call_codec_suspend(codec, true);
-       codec->pm_down_notified = 0;
-       if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) {
+       if (!codec->pm_down_notified &&
+           !bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) {
                codec->pm_down_notified = 1;
                hda_call_pm_notify(bus, false);
        }
index 26ad4f0aade3fcf2c26940c008fae9a55c63244c..b7c89dff7066758e950feec6842dab58f2de29ed 100644 (file)
@@ -4475,9 +4475,11 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
                                            true, &spec->vmaster_mute.sw_kctl);
                if (err < 0)
                        return err;
-               if (spec->vmaster_mute.hook)
+               if (spec->vmaster_mute.hook) {
                        snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
                                                 spec->vmaster_mute_enum);
+                       snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+               }
        }
 
        free_kctls(spec); /* no longer needed */
index 0cbdd87dde6d90ac67767ddd30331c62b8d02862..2aa2f579b4d66857fcf401000ac24ebde3ff6281 100644 (file)
@@ -968,6 +968,15 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
        }
 }
 
+static void ad1884_fixup_thinkpad(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+       struct ad198x_spec *spec = codec->spec;
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+               spec->gen.keep_eapd_on = 1;
+}
+
 /* set magic COEFs for dmic */
 static const struct hda_verb ad1884_dmic_init_verbs[] = {
        {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
@@ -979,6 +988,7 @@ enum {
        AD1884_FIXUP_AMP_OVERRIDE,
        AD1884_FIXUP_HP_EAPD,
        AD1884_FIXUP_DMIC_COEF,
+       AD1884_FIXUP_THINKPAD,
        AD1884_FIXUP_HP_TOUCHSMART,
 };
 
@@ -997,6 +1007,12 @@ static const struct hda_fixup ad1884_fixups[] = {
                .type = HDA_FIXUP_VERBS,
                .v.verbs = ad1884_dmic_init_verbs,
        },
+       [AD1884_FIXUP_THINKPAD] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = ad1884_fixup_thinkpad,
+               .chained = true,
+               .chain_id = AD1884_FIXUP_DMIC_COEF,
+       },
        [AD1884_FIXUP_HP_TOUCHSMART] = {
                .type = HDA_FIXUP_VERBS,
                .v.verbs = ad1884_dmic_init_verbs,
@@ -1008,7 +1024,7 @@ static const struct hda_fixup ad1884_fixups[] = {
 static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
        SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
-       SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF),
+       SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD),
        {}
 };
 
index bf313bea70858f8a4abd18ef6c60beca6668fea1..8ad554312b69196de4d9cf056024c2e8f07907bd 100644 (file)
@@ -4623,6 +4623,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
        SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_ASUS_MODE4),
+       SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_ASUS_MODE4),
        SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
        SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
        SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
index 651ce09236755ff9938062e3e719926fad856f9a..c91eba504f92bc053dd2a51b8f62c7bff6bcc2d6 100644 (file)
@@ -270,7 +270,7 @@ MODULE_DEVICE_TABLE(of, pcm1681_dt_ids);
 static const struct regmap_config pcm1681_regmap = {
        .reg_bits               = 8,
        .val_bits               = 8,
-       .max_register           = ARRAY_SIZE(pcm1681_reg_defaults) + 1,
+       .max_register           = 0x13,
        .reg_defaults           = pcm1681_reg_defaults,
        .num_reg_defaults       = ARRAY_SIZE(pcm1681_reg_defaults),
        .writeable_reg          = pcm1681_writeable_reg,
index 2a8eccf64c76565ab5abb96f3436face0dda4f01..7613181123fe393042643847f4e2b5dbf4c89285 100644 (file)
@@ -188,7 +188,7 @@ MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
 static const struct regmap_config pcm1792a_regmap = {
        .reg_bits               = 8,
        .val_bits               = 8,
-       .max_register           = 24,
+       .max_register           = 23,
        .reg_defaults           = pcm1792a_reg_defaults,
        .num_reg_defaults       = ARRAY_SIZE(pcm1792a_reg_defaults),
        .writeable_reg          = pcm1792a_writeable_reg,
index 6e3f269243e050fe5149e60807c0e41cbf6e4147..64ad84d8a3061e5e4ba824c7178d10f87921c797 100644 (file)
@@ -674,6 +674,8 @@ static const struct snd_soc_dapm_route intercon[] = {
        /* Left Input */
        {"Left Line1L Mux", "single-ended", "LINE1L"},
        {"Left Line1L Mux", "differential", "LINE1L"},
+       {"Left Line1R Mux", "single-ended", "LINE1R"},
+       {"Left Line1R Mux", "differential", "LINE1R"},
 
        {"Left Line2L Mux", "single-ended", "LINE2L"},
        {"Left Line2L Mux", "differential", "LINE2L"},
@@ -690,6 +692,8 @@ static const struct snd_soc_dapm_route intercon[] = {
        /* Right Input */
        {"Right Line1R Mux", "single-ended", "LINE1R"},
        {"Right Line1R Mux", "differential", "LINE1R"},
+       {"Right Line1L Mux", "single-ended", "LINE1L"},
+       {"Right Line1L Mux", "differential", "LINE1L"},
 
        {"Right Line2R Mux", "single-ended", "LINE2R"},
        {"Right Line2R Mux", "differential", "LINE2R"},
index 8b50e5958de5a43030ac854d18c046cf903e5841..01daf655e20b1663fbb92179283dea25ac209ac9 100644 (file)
@@ -530,6 +530,7 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w,
                                hubs->hp_startup_mode);
                        break;
                }
+               break;
 
        case SND_SOC_DAPM_PRE_PMD:
                snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
index c6b743978d5ecdcdf402ddb43df0b7bd0400c422..6b81d0ce2c44ac637188fffdb67f10289f17f67a 100644 (file)
@@ -936,7 +936,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        ssi_private->ssi_phys = res.start;
 
        ssi_private->irq = irq_of_parse_and_map(np, 0);
-       if (ssi_private->irq == NO_IRQ) {
+       if (ssi_private->irq == 0) {
                dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
                return -ENXIO;
        }
index a3d60d4bea4ce8ace84d2860921b5c46d3f00895..a2fd7321b5a9a1bbd321f756af14fd9f071a372f 100644 (file)
@@ -112,7 +112,7 @@ static int imx_mc13783_probe(struct platform_device *pdev)
                return ret;
        }
 
-       if (machine_is_mx31_3ds()) {
+       if (machine_is_mx31_3ds() || machine_is_mx31moboard()) {
                imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4,
                        IMX_AUDMUX_V2_PTCR_SYN,
                        IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) |
index f58bcd85c07fbd8b302c0c3bcd97fd3cf6ed4d0c..57d6941676ffabc509754317337bf109e16376a3 100644 (file)
@@ -600,19 +600,17 @@ static int imx_ssi_probe(struct platform_device *pdev)
        ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
        ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
 
-       ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
-       if (ret)
-               goto failed_pcm_fiq;
+       ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
+       ssi->dma_init = imx_pcm_dma_init(pdev);
 
-       ret = imx_pcm_dma_init(pdev);
-       if (ret)
-               goto failed_pcm_dma;
+       if (ssi->fiq_init && ssi->dma_init) {
+               ret = ssi->fiq_init;
+               goto failed_pcm;
+       }
 
        return 0;
 
-failed_pcm_dma:
-       imx_pcm_fiq_exit(pdev);
-failed_pcm_fiq:
+failed_pcm:
        snd_soc_unregister_component(&pdev->dev);
 failed_register:
        release_mem_region(res->start, resource_size(res));
@@ -628,8 +626,11 @@ static int imx_ssi_remove(struct platform_device *pdev)
        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        struct imx_ssi *ssi = platform_get_drvdata(pdev);
 
-       imx_pcm_dma_exit(pdev);
-       imx_pcm_fiq_exit(pdev);
+       if (!ssi->dma_init)
+               imx_pcm_dma_exit(pdev);
+
+       if (!ssi->fiq_init)
+               imx_pcm_fiq_exit(pdev);
 
        snd_soc_unregister_component(&pdev->dev);
 
index fb1616ba8c5967e1892b4ff6c7e180b2b9447047..560c40fc9ebbb50241e3732f3a76ece064178bf2 100644 (file)
@@ -211,6 +211,8 @@ struct imx_ssi {
        struct imx_dma_data filter_data_rx;
        struct imx_pcm_fiq_params fiq_params;
 
+       int fiq_init;
+       int dma_init;
        int enabled;
 };
 
index daa78a0095facf5ff35d1eb0b6ca61836b7c9210..4a07f7179690d36e3526cb8bcf9c4fc20e2ca509 100644 (file)
@@ -1,6 +1,6 @@
 config SND_OMAP_SOC
        tristate "SoC Audio for the Texas Instruments OMAP chips"
-       depends on (ARCH_OMAP && DMA_OMAP) || (ARCH_ARM && COMPILE_TEST)
+       depends on (ARCH_OMAP && DMA_OMAP) || (ARM && COMPILE_TEST)
        select SND_DMAENGINE_PCM
 
 config SND_OMAP_SOC_DMIC
@@ -26,7 +26,7 @@ config SND_OMAP_SOC_N810
 
 config SND_OMAP_SOC_RX51
        tristate "SoC Audio support for Nokia RX-51"
-       depends on SND_OMAP_SOC && ARCH_ARM && (MACH_NOKIA_RX51 || COMPILE_TEST)
+       depends on SND_OMAP_SOC && ARM && (MACH_NOKIA_RX51 || COMPILE_TEST)
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_TLV320AIC3X
        select SND_SOC_TPA6130A2
index 9cc6986a8cfb347465a9aa442b3b4e79a4f8d9f6..5dd87f4c919e50650e61e8084856138d47f6f119 100644 (file)
@@ -220,8 +220,8 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv,
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
                               struct rsnd_mod *mod,
                               enum rsnd_reg reg);
-#define rsnd_is_gen1(s)                ((s)->info->flags & RSND_GEN1)
-#define rsnd_is_gen2(s)                ((s)->info->flags & RSND_GEN2)
+#define rsnd_is_gen1(s)                (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
+#define rsnd_is_gen2(s)                (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
 
 /*
  *     R-Car ADG
index c17c14c394df88bb0442ccaf51b5735f05455ab5..b2949aed1ac2e9bfd374dff8b0a2483ecf19ddf4 100644 (file)
@@ -1949,7 +1949,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                                w->active ? "active" : "inactive");
 
        list_for_each_entry(p, &w->sources, list_sink) {
-               if (p->connected && !p->connected(w, p->sink))
+               if (p->connected && !p->connected(w, p->source))
                        continue;
 
                if (p->connect)
@@ -3495,6 +3495,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
                if (!w) {
                        dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
                                dai->driver->playback.stream_name);
+                       return -ENOMEM;
                }
 
                w->priv = dai;
@@ -3513,6 +3514,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
                if (!w) {
                        dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
                                dai->driver->capture.stream_name);
+                       return -ENOMEM;
                }
 
                w->priv = dai;
index 8fd9ec66121c111b5bcfc29fd6e0e286f125ad67..b8d6d541d854b7d719056cec96640548980eb79e 100644 (file)
@@ -89,6 +89,7 @@ static char *processor_arch;
 static char *os_build;
 static char *os_version;
 static char *lic_version = "Unknown version";
+static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
 static struct utsname uts_buf;
 
 /*
@@ -1367,7 +1368,7 @@ setval_error:
 }
 
 
-static int
+static void
 kvp_get_domain_name(char *buffer, int length)
 {
        struct addrinfo hints, *info ;
@@ -1381,12 +1382,12 @@ kvp_get_domain_name(char *buffer, int length)
 
        error = getaddrinfo(buffer, NULL, &hints, &info);
        if (error != 0) {
-               strcpy(buffer, "getaddrinfo failed\n");
-               return error;
+               snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",
+                       error, gai_strerror(error));
+               return;
        }
-       strcpy(buffer, info->ai_canonname);
+       snprintf(buffer, length, "%s", info->ai_canonname);
        freeaddrinfo(info);
-       return error;
 }
 
 static int
@@ -1433,7 +1434,6 @@ int main(void)
        int     pool;
        char    *if_name;
        struct hv_kvp_ipaddr_value *kvp_ip_val;
-       char *kvp_send_buffer;
        char *kvp_recv_buffer;
        size_t kvp_recv_buffer_len;
 
@@ -1442,17 +1442,21 @@ int main(void)
        openlog("KVP", 0, LOG_USER);
        syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
 
-       kvp_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg);
-       kvp_send_buffer = calloc(1, kvp_recv_buffer_len);
+       kvp_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg);
        kvp_recv_buffer = calloc(1, kvp_recv_buffer_len);
-       if (!(kvp_send_buffer && kvp_recv_buffer)) {
-               syslog(LOG_ERR, "Failed to allocate netlink buffers");
+       if (!kvp_recv_buffer) {
+               syslog(LOG_ERR, "Failed to allocate netlink buffer");
                exit(EXIT_FAILURE);
        }
        /*
         * Retrieve OS release information.
         */
        kvp_get_os_info();
+       /*
+        * Cache Fully Qualified Domain Name because getaddrinfo takes an
+        * unpredictable amount of time to finish.
+        */
+       kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));
 
        if (kvp_file_init()) {
                syslog(LOG_ERR, "Failed to initialize the pools");
@@ -1488,7 +1492,7 @@ int main(void)
        /*
         * Register ourselves with the kernel.
         */
-       message = (struct cn_msg *)kvp_send_buffer;
+       message = (struct cn_msg *)kvp_recv_buffer;
        message->id.idx = CN_KVP_IDX;
        message->id.val = CN_KVP_VAL;
 
@@ -1671,8 +1675,7 @@ int main(void)
 
                switch (hv_msg->body.kvp_enum_data.index) {
                case FullyQualifiedDomainName:
-                       kvp_get_domain_name(key_value,
-                                       HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+                       strcpy(key_value, full_domain_name);
                        strcpy(key_name, "FullyQualifiedDomainName");
                        break;
                case IntegrationServicesVersion:
index 8611962c672c70dd282443a59cda62f10523ac0f..8bcb04096eb267a1f6d49e672720661e779ec5c3 100644 (file)
@@ -140,7 +140,6 @@ int main(void)
        struct cn_msg   *incoming_cn_msg;
        int     op;
        struct hv_vss_msg *vss_msg;
-       char *vss_send_buffer;
        char *vss_recv_buffer;
        size_t vss_recv_buffer_len;
 
@@ -150,10 +149,9 @@ int main(void)
        openlog("Hyper-V VSS", 0, LOG_USER);
        syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
 
-       vss_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);
-       vss_send_buffer = calloc(1, vss_recv_buffer_len);
+       vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);
        vss_recv_buffer = calloc(1, vss_recv_buffer_len);
-       if (!(vss_send_buffer && vss_recv_buffer)) {
+       if (!vss_recv_buffer) {
                syslog(LOG_ERR, "Failed to allocate netlink buffers");
                exit(EXIT_FAILURE);
        }
@@ -185,7 +183,7 @@ int main(void)
        /*
         * Register ourselves with the kernel.
         */
-       message = (struct cn_msg *)vss_send_buffer;
+       message = (struct cn_msg *)vss_recv_buffer;
        message->id.idx = CN_VSS_IDX;
        message->id.val = CN_VSS_VAL;
        message->ack = 0;
index e297b74471b8a32e76912342f50f77534360ec87..ca0d3d9f4bac831d377fd90f9a38be1021c486ec 100644 (file)
@@ -90,8 +90,20 @@ OPTIONS
        Number of mmap data pages. Must be a power of two.
 
 -g::
+       Enables call-graph (stack chain/backtrace) recording.
+
 --call-graph::
-       Do call-graph (stack chain/backtrace) recording.
+       Setup and enable call-graph (stack chain/backtrace) recording,
+       implies -g.
+
+       Allows specifying "fp" (frame pointer) or "dwarf"
+       (DWARF's CFI - Call Frame Information) as the method to collect
+       the information used to show the call graphs.
+
+       In some systems, where binaries are build with gcc
+       --fomit-frame-pointer, using the "fp" method will produce bogus
+       call graphs, using "dwarf", if available (perf tools linked to
+       the libunwind library) should be used instead.
 
 -q::
 --quiet::
index 58d6598a968679fb4c020f9d932443a57f9feee8..6a118e71d0032e15d3ce8d55b91c24a1be83e31f 100644 (file)
@@ -140,20 +140,12 @@ Default is to monitor all CPUS.
 --asm-raw::
        Show raw instruction encoding of assembly instructions.
 
--G [type,min,order]::
+-G::
+       Enables call-graph (stack chain/backtrace) recording.
+
 --call-graph::
-        Display call chains using type, min percent threshold and order.
-       type can be either:
-       - flat: single column, linear exposure of call chains.
-       - graph: use a graph tree, displaying absolute overhead rates.
-       - fractal: like graph, but displays relative rates. Each branch of
-                the tree is considered as a new profiled object.
-
-       order can be either:
-       - callee: callee based call graph.
-       - caller: inverted caller based call graph.
-
-       Default: fractal,0.5,callee.
+       Setup and enable call-graph (stack chain/backtrace) recording,
+       implies -G.
 
 --ignore-callees=<regex>::
         Ignore callees of the function(s) matching the given regex.
index 935d52216c899eb42774ae9f6e63767ff77fc8ba..fbc2888d64950040d3f56444d11bc0e828396b31 100644 (file)
@@ -888,11 +888,18 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
        while ((event = perf_evlist__mmap_read(kvm->evlist, idx)) != NULL) {
                err = perf_evlist__parse_sample(kvm->evlist, event, &sample);
                if (err) {
+                       perf_evlist__mmap_consume(kvm->evlist, idx);
                        pr_err("Failed to parse sample\n");
                        return -1;
                }
 
                err = perf_session_queue_event(kvm->session, event, &sample, 0);
+               /*
+                * FIXME: Here we can't consume the event, as perf_session_queue_event will
+                *        point to it, and it'll get possibly overwritten by the kernel.
+                */
+               perf_evlist__mmap_consume(kvm->evlist, idx);
+
                if (err) {
                        pr_err("Failed to enqueue sample: %d\n", err);
                        return -1;
index a41ac41546c962df3e0801b230f06b4aa9730c7a..d0465148464022c82e5fcfb431513624edf34740 100644 (file)
@@ -712,21 +712,12 @@ static int get_stack_size(char *str, unsigned long *_size)
 }
 #endif /* LIBUNWIND_SUPPORT */
 
-int record_parse_callchain_opt(const struct option *opt,
-                              const char *arg, int unset)
+int record_parse_callchain(const char *arg, struct perf_record_opts *opts)
 {
-       struct perf_record_opts *opts = opt->value;
        char *tok, *name, *saveptr = NULL;
        char *buf;
        int ret = -1;
 
-       /* --no-call-graph */
-       if (unset)
-               return 0;
-
-       /* We specified default option if none is provided. */
-       BUG_ON(!arg);
-
        /* We need buffer that we know we can write to. */
        buf = malloc(strlen(arg) + 1);
        if (!buf)
@@ -764,13 +755,9 @@ int record_parse_callchain_opt(const struct option *opt,
                                ret = get_stack_size(tok, &size);
                                opts->stack_dump_size = size;
                        }
-
-                       if (!ret)
-                               pr_debug("callchain: stack dump size %d\n",
-                                        opts->stack_dump_size);
 #endif /* LIBUNWIND_SUPPORT */
                } else {
-                       pr_err("callchain: Unknown -g option "
+                       pr_err("callchain: Unknown --call-graph option "
                               "value: %s\n", arg);
                        break;
                }
@@ -778,13 +765,52 @@ int record_parse_callchain_opt(const struct option *opt,
        } while (0);
 
        free(buf);
+       return ret;
+}
+
+static void callchain_debug(struct perf_record_opts *opts)
+{
+       pr_debug("callchain: type %d\n", opts->call_graph);
 
+       if (opts->call_graph == CALLCHAIN_DWARF)
+               pr_debug("callchain: stack dump size %d\n",
+                        opts->stack_dump_size);
+}
+
+int record_parse_callchain_opt(const struct option *opt,
+                              const char *arg,
+                              int unset)
+{
+       struct perf_record_opts *opts = opt->value;
+       int ret;
+
+       /* --no-call-graph */
+       if (unset) {
+               opts->call_graph = CALLCHAIN_NONE;
+               pr_debug("callchain: disabled\n");
+               return 0;
+       }
+
+       ret = record_parse_callchain(arg, opts);
        if (!ret)
-               pr_debug("callchain: type %d\n", opts->call_graph);
+               callchain_debug(opts);
 
        return ret;
 }
 
+int record_callchain_opt(const struct option *opt,
+                        const char *arg __maybe_unused,
+                        int unset __maybe_unused)
+{
+       struct perf_record_opts *opts = opt->value;
+
+       if (opts->call_graph == CALLCHAIN_NONE)
+               opts->call_graph = CALLCHAIN_FP;
+
+       callchain_debug(opts);
+       return 0;
+}
+
 static const char * const record_usage[] = {
        "perf record [<options>] [<command>]",
        "perf record [<options>] -- <command> [<options>]",
@@ -813,12 +839,12 @@ static struct perf_record record = {
        },
 };
 
-#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
+#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "
 
 #ifdef LIBUNWIND_SUPPORT
-const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
+const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf";
 #else
-const char record_callchain_help[] = CALLCHAIN_HELP "[fp]";
+const char record_callchain_help[] = CALLCHAIN_HELP "fp";
 #endif
 
 /*
@@ -858,9 +884,12 @@ const struct option record_options[] = {
                     "number of mmap data pages"),
        OPT_BOOLEAN(0, "group", &record.opts.group,
                    "put the counters into a counter group"),
-       OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts,
-                            "mode[,dump_size]", record_callchain_help,
-                            &record_parse_callchain_opt, "fp"),
+       OPT_CALLBACK_NOOPT('g', NULL, &record.opts,
+                          NULL, "enables call-graph recording" ,
+                          &record_callchain_opt),
+       OPT_CALLBACK(0, "call-graph", &record.opts,
+                    "mode[,dump_size]", record_callchain_help,
+                    &record_parse_callchain_opt),
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show counter open errors, etc)"),
        OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
index 212214162bb2820286694417035e470e1d465e76..5a11f13e56f9f186cc7aa0926f4ee63008327358 100644 (file)
@@ -810,7 +810,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                ret = perf_evlist__parse_sample(top->evlist, event, &sample);
                if (ret) {
                        pr_err("Can't parse sample, err = %d\n", ret);
-                       continue;
+                       goto next_event;
                }
 
                evsel = perf_evlist__id2evsel(session->evlist, sample.id);
@@ -825,13 +825,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                case PERF_RECORD_MISC_USER:
                        ++top->us_samples;
                        if (top->hide_user_symbols)
-                               continue;
+                               goto next_event;
                        machine = &session->machines.host;
                        break;
                case PERF_RECORD_MISC_KERNEL:
                        ++top->kernel_samples;
                        if (top->hide_kernel_symbols)
-                               continue;
+                               goto next_event;
                        machine = &session->machines.host;
                        break;
                case PERF_RECORD_MISC_GUEST_KERNEL:
@@ -847,7 +847,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                         */
                        /* Fall thru */
                default:
-                       continue;
+                       goto next_event;
                }
 
 
@@ -859,6 +859,8 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                        machine__process_event(machine, event);
                } else
                        ++session->stats.nr_unknown_events;
+next_event:
+               perf_evlist__mmap_consume(top->evlist, idx);
        }
 }
 
@@ -1016,16 +1018,16 @@ out_delete:
 }
 
 static int
-parse_callchain_opt(const struct option *opt, const char *arg, int unset)
+callchain_opt(const struct option *opt, const char *arg, int unset)
 {
-       /*
-        * --no-call-graph
-        */
-       if (unset)
-               return 0;
-
        symbol_conf.use_callchain = true;
+       return record_callchain_opt(opt, arg, unset);
+}
 
+static int
+parse_callchain_opt(const struct option *opt, const char *arg, int unset)
+{
+       symbol_conf.use_callchain = true;
        return record_parse_callchain_opt(opt, arg, unset);
 }
 
@@ -1106,9 +1108,12 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
                   "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"),
        OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
                    "Show a column with the number of samples"),
-       OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts,
-                            "mode[,dump_size]", record_callchain_help,
-                            &parse_callchain_opt, "fp"),
+       OPT_CALLBACK_NOOPT('G', NULL, &top.record_opts,
+                          NULL, "enables call-graph recording",
+                          &callchain_opt),
+       OPT_CALLBACK(0, "call-graph", &top.record_opts,
+                    "mode[,dump_size]", record_callchain_help,
+                    &parse_callchain_opt),
        OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
                   "ignore callees of these functions in call graphs",
                   report_parse_ignore_callees_opt),
index 71aa3e35406bd064e87044d2ae71597a5f117092..99c8d9ad6729cabf6bebe5b65c753af1a28d9024 100644 (file)
@@ -987,7 +987,7 @@ again:
                        err = perf_evlist__parse_sample(evlist, event, &sample);
                        if (err) {
                                fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err);
-                               continue;
+                               goto next_event;
                        }
 
                        if (trace->base_time == 0)
@@ -1001,18 +1001,20 @@ again:
                        evsel = perf_evlist__id2evsel(evlist, sample.id);
                        if (evsel == NULL) {
                                fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
-                               continue;
+                               goto next_event;
                        }
 
                        if (sample.raw_data == NULL) {
                                fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
                                       perf_evsel__name(evsel), sample.tid,
                                       sample.cpu, sample.raw_size);
-                               continue;
+                               goto next_event;
                        }
 
                        handler = evsel->handler.func;
                        handler(trace, evsel, &sample);
+next_event:
+                       perf_evlist__mmap_consume(evlist, i);
 
                        if (done)
                                goto out_unmap_evlist;
index 6fb781d5586cd1d264bc223375f081e0b0ed0c82..e3fedfa2906e5912d6dac6da9ef73daa53820b6d 100644 (file)
@@ -290,6 +290,7 @@ static int process_events(struct machine *machine, struct perf_evlist *evlist,
        for (i = 0; i < evlist->nr_mmaps; i++) {
                while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
                        ret = process_event(machine, evlist, event, state);
+                       perf_evlist__mmap_consume(evlist, i);
                        if (ret < 0)
                                return ret;
                }
index d444ea2c47d9d03d5bacb01a35ac9288ca5971df..376c35608534a41dce87354c1741e5b29fbdd612 100644 (file)
@@ -36,6 +36,7 @@ static int find_comm(struct perf_evlist *evlist, const char *comm)
                            (pid_t)event->comm.tid == getpid() &&
                            strcmp(event->comm.comm, comm) == 0)
                                found += 1;
+                       perf_evlist__mmap_consume(evlist, i);
                }
        }
        return found;
index c4185b9aeb80e217e4e489ef3eb49d0035557f84..a7232c204eb94303173fad65a13194af233d118d 100644 (file)
@@ -122,6 +122,7 @@ int test__basic_mmap(void)
                        goto out_munmap;
                }
                nr_events[evsel->idx]++;
+               perf_evlist__mmap_consume(evlist, 0);
        }
 
        err = 0;
index fc5b9fca8b47421e13b4fe1c38e07f267be5ade1..524b221b829bebd1a9b6f97109e9e6a78c20ef48 100644 (file)
@@ -77,8 +77,10 @@ int test__syscall_open_tp_fields(void)
 
                                ++nr_events;
 
-                               if (type != PERF_RECORD_SAMPLE)
+                               if (type != PERF_RECORD_SAMPLE) {
+                                       perf_evlist__mmap_consume(evlist, i);
                                        continue;
+                               }
 
                                err = perf_evsel__parse_sample(evsel, event, &sample);
                                if (err) {
index b8a7056519ac7c29b430a87d73be85314b4d8082..7923b06ffc9198adde790901c8709dd8e66fa0b7 100644 (file)
@@ -263,6 +263,8 @@ int test__PERF_RECORD(void)
                                                 type);
                                        ++errs;
                                }
+
+                               perf_evlist__mmap_consume(evlist, i);
                        }
                }
 
index 0ab61b1f408ecfc71680b00b8f0e7f0ba6911b0d..4ca1b938f6a620380f8cae76435f8ad6b142eecd 100644 (file)
@@ -122,7 +122,7 @@ int test__perf_time_to_tsc(void)
                        if (event->header.type != PERF_RECORD_COMM ||
                            (pid_t)event->comm.pid != getpid() ||
                            (pid_t)event->comm.tid != getpid())
-                               continue;
+                               goto next_event;
 
                        if (strcmp(event->comm.comm, comm1) == 0) {
                                CHECK__(perf_evsel__parse_sample(evsel, event,
@@ -134,6 +134,8 @@ int test__perf_time_to_tsc(void)
                                                                 &sample));
                                comm2_time = sample.time;
                        }
+next_event:
+                       perf_evlist__mmap_consume(evlist, i);
                }
        }
 
index 2e41e2d32ccc48d3946086dbf3d734d736e3193f..6e2b44ec07495b716a45f649206f5088b2aea51f 100644 (file)
@@ -78,7 +78,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
                struct perf_sample sample;
 
                if (event->header.type != PERF_RECORD_SAMPLE)
-                       continue;
+                       goto next_event;
 
                err = perf_evlist__parse_sample(evlist, event, &sample);
                if (err < 0) {
@@ -88,6 +88,8 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
 
                total_periods += sample.period;
                nr_samples++;
+next_event:
+               perf_evlist__mmap_consume(evlist, 0);
        }
 
        if ((u64) nr_samples == total_periods) {
index 28fe5894b0618901493100ea0995c8dda093b9ec..a3e64876e940020d93aa674ea064d735bf688713 100644 (file)
@@ -96,10 +96,10 @@ int test__task_exit(void)
 
 retry:
        while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) {
-               if (event->header.type != PERF_RECORD_EXIT)
-                       continue;
+               if (event->header.type == PERF_RECORD_EXIT)
+                       nr_exit++;
 
-               nr_exit++;
+               perf_evlist__mmap_consume(evlist, 0);
        }
 
        if (!exited || !nr_exit) {
index 194e2f42ff5d1335eb1c2b5b53ea2ab6b1604932..6c152686e83763a9b18983d06f52e2a14b739b91 100644 (file)
@@ -315,8 +315,7 @@ static inline void advance_hpp(struct perf_hpp *hpp, int inc)
 }
 
 static int hist_entry__period_snprintf(struct perf_hpp *hpp,
-                                      struct hist_entry *he,
-                                      bool color)
+                                      struct hist_entry *he)
 {
        const char *sep = symbol_conf.field_sep;
        struct perf_hpp_fmt *fmt;
@@ -338,7 +337,7 @@ static int hist_entry__period_snprintf(struct perf_hpp *hpp,
                } else
                        first = false;
 
-               if (color && fmt->color)
+               if (perf_hpp__use_color() && fmt->color)
                        ret = fmt->color(fmt, hpp, he);
                else
                        ret = fmt->entry(fmt, hpp, he);
@@ -358,12 +357,11 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
                .buf            = bf,
                .size           = size,
        };
-       bool color = !symbol_conf.field_sep;
 
        if (size == 0 || size > bfsz)
                size = hpp.size = bfsz;
 
-       ret = hist_entry__period_snprintf(&hpp, he, color);
+       ret = hist_entry__period_snprintf(&hpp, he);
        hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
 
        ret = fprintf(fp, "%s\n", bf);
@@ -482,6 +480,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
 
 print_entries:
        linesz = hists__sort_list_width(hists) + 3 + 1;
+       linesz += perf_hpp__color_overhead();
        line = malloc(linesz);
        if (line == NULL) {
                ret = -1;
index 2b585bc308cff6f5f7cdfa1685c469007b0db3b5..9e99060408ae759e39401f686300090b76e52edf 100644 (file)
@@ -147,6 +147,9 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
 
 struct option;
 
+int record_parse_callchain(const char *arg, struct perf_record_opts *opts);
 int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset);
+int record_callchain_opt(const struct option *opt, const char *arg, int unset);
+
 extern const char record_callchain_help[];
 #endif /* __PERF_CALLCHAIN_H */
index 9b393e7dca6fe849037d5a1314a8cf6b22e1f596..49096ea58a15dec22dc1e20b0de5d69bf2c2ac3a 100644 (file)
@@ -187,7 +187,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                return -1;
        }
 
-       event->header.type = PERF_RECORD_MMAP2;
+       event->header.type = PERF_RECORD_MMAP;
        /*
         * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
         */
@@ -198,7 +198,6 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                char prot[5];
                char execname[PATH_MAX];
                char anonstr[] = "//anon";
-               unsigned int ino;
                size_t size;
                ssize_t n;
 
@@ -209,15 +208,12 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                strcpy(execname, "");
 
                /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
-               n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n",
-                      &event->mmap2.start, &event->mmap2.len, prot,
-                      &event->mmap2.pgoff, &event->mmap2.maj,
-                      &event->mmap2.min,
-                      &ino, execname);
-
-               event->mmap2.ino = (u64)ino;
+               n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n",
+                      &event->mmap.start, &event->mmap.len, prot,
+                      &event->mmap.pgoff,
+                      execname);
 
-               if (n != 8)
+               if (n != 5)
                        continue;
 
                if (prot[2] != 'x')
@@ -227,15 +223,15 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                        strcpy(execname, anonstr);
 
                size = strlen(execname) + 1;
-               memcpy(event->mmap2.filename, execname, size);
+               memcpy(event->mmap.filename, execname, size);
                size = PERF_ALIGN(size, sizeof(u64));
-               event->mmap2.len -= event->mmap.start;
-               event->mmap2.header.size = (sizeof(event->mmap2) -
-                                       (sizeof(event->mmap2.filename) - size));
-               memset(event->mmap2.filename + size, 0, machine->id_hdr_size);
-               event->mmap2.header.size += machine->id_hdr_size;
-               event->mmap2.pid = tgid;
-               event->mmap2.tid = pid;
+               event->mmap.len -= event->mmap.start;
+               event->mmap.header.size = (sizeof(event->mmap) -
+                                       (sizeof(event->mmap.filename) - size));
+               memset(event->mmap.filename + size, 0, machine->id_hdr_size);
+               event->mmap.header.size += machine->id_hdr_size;
+               event->mmap.pid = tgid;
+               event->mmap.tid = pid;
 
                if (process(tool, event, &synth_sample, machine) != 0) {
                        rc = -1;
index f9f77bee0b1b416414fa3561fb2eee4785e502c4..e584cd30b0f2dca4866919e578e4c8e9e3f71ade 100644 (file)
@@ -545,12 +545,19 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
 
        md->prev = old;
 
-       if (!evlist->overwrite)
-               perf_mmap__write_tail(md, old);
-
        return event;
 }
 
+void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx)
+{
+       if (!evlist->overwrite) {
+               struct perf_mmap *md = &evlist->mmap[idx];
+               unsigned int old = md->prev;
+
+               perf_mmap__write_tail(md, old);
+       }
+}
+
 static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
 {
        if (evlist->mmap[idx].base != NULL) {
index 880d7139d2fb3fce78d75b8e701251827045588e..206d093393069012421a65b6dc893f570de32dd9 100644 (file)
@@ -89,6 +89,8 @@ struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id);
 
 union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx);
 
+void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx);
+
 int perf_evlist__open(struct perf_evlist *evlist);
 void perf_evlist__close(struct perf_evlist *evlist);
 
index 0ce9febf1ba0c8c1a691c74a0c55fa1c4a8dfd14..9f1ef9bee2d0c96583dac31d4c12bcc220b519bf 100644 (file)
@@ -678,7 +678,6 @@ void perf_evsel__config(struct perf_evsel *evsel,
                attr->sample_type       |= PERF_SAMPLE_WEIGHT;
 
        attr->mmap  = track;
-       attr->mmap2 = track && !perf_missing_features.mmap2;
        attr->comm  = track;
 
        /*
index 1329b6b6ffe61b0f5fb97d8582a837f554b3c752..ce8dc61ce2c358ddea5851079916292e6838a370 100644 (file)
@@ -5,6 +5,7 @@
 #include <pthread.h>
 #include "callchain.h"
 #include "header.h"
+#include "color.h"
 
 extern struct callchain_param callchain_param;
 
@@ -175,6 +176,18 @@ void perf_hpp__init(void);
 void perf_hpp__column_register(struct perf_hpp_fmt *format);
 void perf_hpp__column_enable(unsigned col);
 
+static inline size_t perf_hpp__use_color(void)
+{
+       return !symbol_conf.field_sep;
+}
+
+static inline size_t perf_hpp__color_overhead(void)
+{
+       return perf_hpp__use_color() ?
+              (COLOR_MAXLEN + sizeof(PERF_COLOR_RESET)) * PERF_HPP__MAX_INDEX
+              : 0;
+}
+
 struct perf_evlist;
 
 struct hist_browser_timer {
index c09e0a9fdf4cda118be077fbee1fb382dcc3d5ee..f0692737ebf1237373a140c0ec76a7a4a7a96f9d 100644 (file)
@@ -1357,10 +1357,10 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,
                        goto post;
                }
 
+               fname = dwarf_decl_file(&spdie);
                if (addr == (unsigned long)baseaddr) {
                        /* Function entry - Relative line number is 0 */
                        lineno = baseline;
-                       fname = dwarf_decl_file(&spdie);
                        goto post;
                }
 
index 71b5412bbbb9d1197dc5ae999c94704211108238..2ac4bc92bb1ff2b130e47a59e43c3127ba0568b7 100644 (file)
@@ -822,6 +822,8 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
                PyObject *pyevent = pyrf_event__new(event);
                struct pyrf_event *pevent = (struct pyrf_event *)pyevent;
 
+               perf_evlist__mmap_consume(evlist, cpu);
+
                if (pyevent == NULL)
                        return PyErr_NoMemory();
 
index a85e4ae5f3ac582381740f8b29460e6f83ae6bd6..c0c9795c4f0235b51848e01db33a7950069b182a 100644 (file)
@@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,
 
        event = find_cache_event(evsel);
        if (!event)
-               die("ug! no event found for type %" PRIu64, evsel->attr.config);
+               die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
 
        pid = raw_field_value(event, "common_pid", data);
 
index cc75a3cef388065f3164fe270487d2b06d394c72..95d91a0b23afe01a45463a02dcc8eadbb5ba5169 100644 (file)
@@ -56,6 +56,17 @@ static void handler_call_die(const char *handler_name)
        Py_FatalError("problem in Python trace event handler");
 }
 
+/*
+ * Insert val into into the dictionary and decrement the reference counter.
+ * This is necessary for dictionaries since PyDict_SetItemString() does not 
+ * steal a reference, as opposed to PyTuple_SetItem().
+ */
+static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObject *val)
+{
+       PyDict_SetItemString(dict, key, val);
+       Py_DECREF(val);
+}
+
 static void define_value(enum print_arg_type field_type,
                         const char *ev_name,
                         const char *field_name,
@@ -279,11 +290,11 @@ static void python_process_tracepoint(union perf_event *perf_event
                PyTuple_SetItem(t, n++, PyInt_FromLong(pid));
                PyTuple_SetItem(t, n++, PyString_FromString(comm));
        } else {
-               PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu));
-               PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s));
-               PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns));
-               PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid));
-               PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm));
+               pydict_set_item_string_decref(dict, "common_cpu", PyInt_FromLong(cpu));
+               pydict_set_item_string_decref(dict, "common_s", PyInt_FromLong(s));
+               pydict_set_item_string_decref(dict, "common_ns", PyInt_FromLong(ns));
+               pydict_set_item_string_decref(dict, "common_pid", PyInt_FromLong(pid));
+               pydict_set_item_string_decref(dict, "common_comm", PyString_FromString(comm));
        }
        for (field = event->format.fields; field; field = field->next) {
                if (field->flags & FIELD_IS_STRING) {
@@ -313,7 +324,7 @@ static void python_process_tracepoint(union perf_event *perf_event
                if (handler)
                        PyTuple_SetItem(t, n++, obj);
                else
-                       PyDict_SetItemString(dict, field->name, obj);
+                       pydict_set_item_string_decref(dict, field->name, obj);
 
        }
        if (!handler)
@@ -370,21 +381,21 @@ static void python_process_general_event(union perf_event *perf_event
        if (!handler || !PyCallable_Check(handler))
                goto exit;
 
-       PyDict_SetItemString(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel)));
-       PyDict_SetItemString(dict, "attr", PyString_FromStringAndSize(
+       pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel)));
+       pydict_set_item_string_decref(dict, "attr", PyString_FromStringAndSize(
                        (const char *)&evsel->attr, sizeof(evsel->attr)));
-       PyDict_SetItemString(dict, "sample", PyString_FromStringAndSize(
+       pydict_set_item_string_decref(dict, "sample", PyString_FromStringAndSize(
                        (const char *)sample, sizeof(*sample)));
-       PyDict_SetItemString(dict, "raw_buf", PyString_FromStringAndSize(
+       pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize(
                        (const char *)sample->raw_data, sample->raw_size));
-       PyDict_SetItemString(dict, "comm",
+       pydict_set_item_string_decref(dict, "comm",
                        PyString_FromString(thread->comm));
        if (al->map) {
-               PyDict_SetItemString(dict, "dso",
+               pydict_set_item_string_decref(dict, "dso",
                        PyString_FromString(al->map->dso->name));
        }
        if (al->sym) {
-               PyDict_SetItemString(dict, "symbol",
+               pydict_set_item_string_decref(dict, "symbol",
                        PyString_FromString(al->sym->name));
        }
 
index a9dd682cf5e3f5117de017156396337a8352914f..1cf9ccb010131d42c3a573079437d87502ac016f 100644 (file)
@@ -3091,7 +3091,7 @@ static const struct file_operations *stat_fops[] = {
 
 static int kvm_init_debug(void)
 {
-       int r = -EFAULT;
+       int r = -EEXIST;
        struct kvm_stats_debugfs_item *p;
 
        kvm_debugfs_dir = debugfs_create_dir("kvm", NULL);